org.syncany.connection.plugins.box.BoxTransferManager.java Source code

Java tutorial

Introduction

Here is the source code for org.syncany.connection.plugins.box.BoxTransferManager.java

Source

/*
 * Syncany, www.syncany.org
 * Copyright (C) 2011 Philipp C. Heckel <philipp.heckel@gmail.com> 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.syncany.connection.plugins.box;

import cn.com.believer.songyuanframework.openapi.storage.box.BoxExternalAPI;
import cn.com.believer.songyuanframework.openapi.storage.box.constant.BoxConstant;
import cn.com.believer.songyuanframework.openapi.storage.box.factories.BoxRequestFactory;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.DeleteRequest;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.DeleteResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.DownloadRequest;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.DownloadResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.GetAccountTreeResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.GetAuthTokenRequest;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.GetAuthTokenResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.GetTicketRequest;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.GetTicketResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.RenameResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.UploadRequest;
import cn.com.believer.songyuanframework.openapi.storage.box.functions.UploadResponse;
import cn.com.believer.songyuanframework.openapi.storage.box.impl.simple.SimpleBoxImpl;
import cn.com.believer.songyuanframework.openapi.storage.box.objects.BoxException;
import cn.com.believer.songyuanframework.openapi.storage.box.objects.UploadResult;
import java.io.ByteArrayInputStream;
import org.syncany.connection.plugins.AbstractTransferManager;
import org.syncany.exceptions.StorageConnectException;
import org.syncany.repository.files.RemoteFile;
import org.syncany.exceptions.StorageException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.codec.binary.Base64;
import org.syncany.util.FileUtil;
import org.syncany.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 *
 * @see http://code.google.com/p/box4j/wiki/Box4J_Examples
 * @author Philipp C. Heckel <philipp.heckel@gmail.com>
 */
public class BoxTransferManager extends AbstractTransferManager {
    private BoxExternalAPI box;

    public BoxTransferManager(BoxConnection connection) {
        super(connection);
        box = new SimpleBoxImpl();
    }

    @Override
    public BoxConnection getConnection() {
        return (BoxConnection) super.getConnection();
    }

    @Override
    public void connect() throws StorageConnectException {
        try {
            // Get a ticket by API key.
            if (getConnection().getTicket() == null) {
                GetTicketRequest getTicketRequest = BoxRequestFactory
                        .createGetTicketRequest(getConnection().getApiKey());
                GetTicketResponse getTicketResponse = box.getTicket(getTicketRequest);

                getConnection().setTicket(getTicketResponse.getTicket());
                System.out.println("ticket : " + getTicketResponse.getTicket());
            }

            // Auth Token
            if (getConnection().getToken() == null) {
                GetAuthTokenRequest getAuthTokenRequest = BoxRequestFactory
                        .createGetAuthTokenRequest(getConnection().getApiKey(), getConnection().getTicket());

                GetAuthTokenResponse getAuthTokenResponse = box.getAuthToken(getAuthTokenRequest);
                getConnection().setLoginStatus(getAuthTokenResponse.getStatus());

                System.out.println("token : " + getAuthTokenResponse.getAuthToken());

                if (BoxConstant.STATUS_NOT_LOGGED_IN.equals(getAuthTokenResponse.getStatus())) {
                    throw new StorageConnectException(
                            "Could not log in to box.net. Token is not logged in. Log in at https://www.box.net/api/1.0/auth/"
                                    + getConnection().getTicket());
                }

                if (!BoxConstant.STATUS_GET_AUTH_TOKEN_OK.equals(getAuthTokenResponse.getStatus())) {
                    throw new StorageConnectException(
                            "Could not log in to box.net. Status = " + getAuthTokenResponse.getStatus());
                }

                getConnection().setToken(getAuthTokenResponse.getAuthToken());
            }
        } catch (Exception ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
            throw new StorageConnectException(ex.getMessage());
        }
    }

    @Override
    public void disconnect() {
        // Fressen.
    }

    @Override
    public void download(RemoteFile remoteFile, File localFile) throws StorageException {
        connect();

        File tmpLocalFile;

        try {
            // Find file on storage
            RemoteFile foundRemoteFile = find(remoteFile);
            String remoteFileId = getFileId(foundRemoteFile);

            if (remoteFileId == null) {
                throw new StorageException("Could not find file " + remoteFile);
            }

            // Download it
            // Note: The "DownloadRequest" option cannot tell whether the transfer 
            //       was successful. That's why we do it like the API says.

            tmpLocalFile = config.getCache().createTempFile();

            URL fileURL = new URL(
                    "https://www.box.net/api/1.0/download/" + getConnection().getToken() + "/" + remoteFileId);
            InputStream is = fileURL.openStream();

            FileUtil.writeFile(is, tmpLocalFile);

            // Rename
            if (!tmpLocalFile.renameTo(localFile)) {
                throw new StorageException(
                        "Renaming downloaded file from " + tmpLocalFile + " to " + localFile + " failed.");
            }
        } catch (IOException ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BoxException ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void upload(File localFile, RemoteFile remoteFile) throws StorageException {
        connect();

        try {
            // 0. Find; and skip if exists
            // TODO sloooow
            Object existingRemoteFile = find(remoteFile);

            if (existingRemoteFile != null) {
                return;
            }

            // 1. Upload
            Map<String, File> fileMap = new HashMap();
            fileMap.put(remoteFile.getName(), localFile);

            UploadRequest uploadRequest = BoxRequestFactory.createUploadRequest(getConnection().getToken(), true,
                    getConnection().getFolderId(), fileMap);

            UploadResponse uploadResponse = box.upload(uploadRequest);

            if (!"upload_ok".equals(uploadResponse.getStatus()) || uploadResponse.getUploadResultList().isEmpty()) {
                throw new StorageException("Unable to upload file " + remoteFile.getName() + ". Status code: "
                        + uploadResponse.getStatus());
            }

            // 2. Rename (if necessary)
            // The API doesn't allow to specify a chosen name, i.e. a rename might be necessary
            if (!localFile.getName().equals(remoteFile.getName())) {
                UploadResult uploadedFile = (UploadResult) uploadResponse.getUploadResultList().get(0);
                String boxFileId = uploadedFile.getFile().getFileId();

                RenameResponse renameResponse = box
                        .rename(BoxRequestFactory.createRenameRequest(getConnection().getApiKey(),
                                getConnection().getToken(), "file", boxFileId, remoteFile.getName()));

                // Destination exists; that's okay: delete uploaded file!
                if ("e_rename_node".equals(renameResponse.getStatus())) {
                    box.delete(BoxRequestFactory.createDeleteRequest(getConnection().getApiKey(),
                            getConnection().getToken(), "ile", boxFileId));
                }

                // Other error
                else if (!"s_rename_node".equals(renameResponse.getStatus())) {
                    throw new StorageException("Unable to upload file " + remoteFile.getName()
                            + ". Rename failed. Status code: " + renameResponse.getStatus());
                }
            }
        } catch (Exception ex) {
            System.err.println("error uploading: " + ex.getMessage());
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
            throw new StorageException(ex);
        }
    }

    @Override
    public Map<String, RemoteFile> list() throws StorageException {
        connect();

        try {
            GetAccountTreeResponse accountTreeResponse = box
                    .getAccountTree(BoxRequestFactory.createGetAccountTreeRequest(getConnection().getApiKey(),
                            getConnection().getToken(), getConnection().getFolderId(), new String[] {}));

            if (!"listing_ok".equals(accountTreeResponse.getStatus())) {
                throw new StorageException("List query failed. Status: " + accountTreeResponse.getStatus());
            }

            // Note:
            //   The result is a (1) base 64 encoded, (2) zipped (3) XML file
            //   See http://developers.box.net/w/page/12923929/ApiFunction_get_account_tree

            // (1) Decode Base 64
            String base64Xml = accountTreeResponse.getEncodedTree();
            InputStream decodedBase64 = new ByteArrayInputStream(Base64.decodeBase64(base64Xml));

            // (2) Unzip
            ZipInputStream zip = new ZipInputStream(decodedBase64);

            if (zip.getNextEntry() == null) {
                throw new StorageException("Cannot read folder tree. Invalid ZIP data.");
            }

            // (3) Read XML            
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(zip);

            // Find files via XPath
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();

            Object result = xpath.evaluate("files/file", doc.getDocumentElement(), XPathConstants.NODESET);

            NodeList nodes = (NodeList) result;
            Map<String, RemoteFile> list = new HashMap<String, RemoteFile>();

            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);

                String name = node.getAttributes().getNamedItem("file_name").getTextContent();
                Integer size = StringUtil.parseInt(node.getAttributes().getNamedItem("size").getTextContent(), -1);

                list.put(name, new RemoteFile(name, size, node));
            }

            return list;
        } catch (Exception ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
            throw new StorageException("...");
        }
    }

    @Override
    public void delete(RemoteFile remoteFile) throws StorageException {
        try {
            RemoteFile foundRemoteFile = find(remoteFile);
            String remoteFileId = getFileId(foundRemoteFile);

            if (remoteFileId == null) {
                return;
            }

            DeleteRequest request = BoxRequestFactory.createDeleteRequest(getConnection().getApiKey(),
                    getConnection().getToken(), "file", remoteFileId);

            DeleteResponse response = box.delete(request);

            if (!"s_delete_node".equals(response.getStatus())) {
                throw new StorageException("Unable to delete file " + remoteFile + " (file id " + remoteFileId
                        + "): status = " + response.getStatus());
            }
        } catch (BoxException ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(BoxTransferManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private RemoteFile find(RemoteFile remoteFile) throws BoxException, IOException, StorageException {
        return list().get(remoteFile.getName());
    }

    private String getFileId(RemoteFile remoteFile) {
        if (remoteFile == null || remoteFile.getSource() == null || !(remoteFile.getSource() instanceof Node)) {

            return null;
        }

        Node node = (Node) remoteFile.getSource();
        return node.getAttributes().getNamedItem("id").getTextContent();
    }
}