de.dhbw_mannheim.cloudraid.dropbox.impl.net.connector.DropboxConnector.java Source code

Java tutorial

Introduction

Here is the source code for de.dhbw_mannheim.cloudraid.dropbox.impl.net.connector.DropboxConnector.java

Source

/*
 * Copyright 2011 - 2012 by the CloudRAID Team
 * see AUTHORS for more details
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
    
 * http://www.apache.org/licenses/LICENSE-2.0
    
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package de.dhbw_mannheim.cloudraid.dropbox.impl.net.connector;

import static org.scribe.model.Verb.GET;
import static org.scribe.model.Verb.POST;
import static org.scribe.model.Verb.PUT;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import javax.activation.MimetypesFileTypeMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.DropBoxApi;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;

import de.dhbw_mannheim.cloudraid.config.ICloudRAIDConfig;
import de.dhbw_mannheim.cloudraid.config.exceptions.MissingConfigValueException;
import de.dhbw_mannheim.cloudraid.core.net.connector.IStorageConnector;

/**
 * The API wrapper for Dropbox (API version 1).
 * 
 * @author Florian Bausch
 * 
 */
public class DropboxConnector implements IStorageConnector {
    private static final String ROOT_NAME = "sandbox";
    private static final String DELETE_URL = "https://api.dropbox.com/1/fileops/delete?root="
            + DropboxConnector.ROOT_NAME + "&path=";
    private static final String GET_URL = "https://api-content.dropbox.com/1/files/" + DropboxConnector.ROOT_NAME
            + "/";
    private static final String PUT_URL = "https://api-content.dropbox.com/1/files_put/"
            + DropboxConnector.ROOT_NAME + "/";
    private static final String META_URL = "https://api.dropbox.com/1/metadata/" + DropboxConnector.ROOT_NAME + "/";
    private static final String RESTORE_URL = "https://api.dropbox.com/1/restore/" + DropboxConnector.ROOT_NAME
            + "/";
    private static final String REVISION_URL = "https://api.dropbox.com/1/revisions/" + DropboxConnector.ROOT_NAME
            + "/";

    private final static MimetypesFileTypeMap MIME_MAP = new MimetypesFileTypeMap();

    /**
     * A reference to the current config;
     */
    private ICloudRAIDConfig config = null;

    private String accessTokenValue = null;
    private String appKey = null;
    private String appSecret = null;
    private String accessTokenSecret = null;
    private String splitOutputDir = null;

    private OAuthService service = null;

    private Token accessToken = null;
    private int id = -1;

    @Override
    public boolean connect() {
        this.service = new ServiceBuilder().provider(DropBoxApi.class).apiKey(this.appKey).apiSecret(this.appSecret)
                .build();
        if (this.accessTokenValue == null || this.accessTokenSecret == null) {
            Token requestToken = this.service.getRequestToken();
            System.out.println("Please go to " + this.service.getAuthorizationUrl(requestToken)
                    + " , authorize the app and then press enter in this window.");
            try {
                Thread.sleep(9000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            Verifier verifier = new Verifier("");
            this.accessToken = this.service.getAccessToken(requestToken, verifier);
            this.accessTokenSecret = this.accessToken.getSecret();
            this.accessTokenValue = this.accessToken.getToken();
            this.config.put(String.format("connector.%d.accessTokenValue", this.id), this.accessTokenValue, true);
            this.config.put(String.format("connector.%d.accessTokenSecret", this.id), this.accessTokenSecret, true);
            this.config.save();
            System.out.println("Your secret access token: " + this.accessTokenSecret);
            System.out.println("Your public access token: " + this.accessTokenValue);
        } else {
            this.accessToken = new Token(this.accessTokenValue, this.accessTokenSecret);
        }
        return true;
    }

    /**
     * This function initializes the {@link DropboxConnector} with the customer
     * and application tokens. During the {@link #connect()} process various
     * tokens are used. If {@link #connect()} returns <code>false</code>, this
     * class has to be re-instantiated and initialized with proper credentials.
     * </br>
     * 
     * The {@link ICloudRAIDConfig} must contain following keys:
     * <ul>
     * <li><code>connector.ID.appKey</code></li>
     * <li><code>connector.ID.appSecret</code></li>
     * <li><i><code>connector.ID.accessTokenValue</code></i> (optional)</li>
     * <li><i><code>connector.ID.accessTokenSecret</code></i> (optional)</li>
     * </ul>
     * 
     * @param connectorid
     *            The internal id of this connector.
     * @param config
     *            The reference to a running {@link ICloudRAIDConfig} service.
     * 
     * @throws InstantiationException
     *             Thrown if not all required parameters are passed.
     */
    @Override
    public IStorageConnector create(int connectorid, ICloudRAIDConfig config) throws InstantiationException {
        this.id = connectorid;
        this.config = config;
        String kAccessTokenSecret = String.format("connector.%d.accessTokenSecret", this.id);
        String kAccessTokenValue = String.format("connector.%d.accessTokenValue", this.id);
        String kAppKey = String.format("connector.%d.appKey", this.id);
        String kAppSecret = String.format("connector.%d.appSecret", this.id);
        try {
            this.splitOutputDir = this.config.getString("split.output.dir");
            if (this.config.keyExists(kAccessTokenSecret) && this.config.keyExists(kAccessTokenValue)
                    && this.config.keyExists(kAppKey) && this.config.keyExists(kAppSecret)) {
                this.accessTokenSecret = this.config.getString(kAccessTokenSecret);
                this.accessTokenValue = this.config.getString(kAccessTokenValue);
                this.appKey = this.config.getString(kAppKey);
                this.appSecret = this.config.getString(kAppSecret);
            } else if (this.config.keyExists(kAppKey) && this.config.keyExists(kAppSecret)) {
                this.appKey = this.config.getString(kAppKey);
                this.appSecret = this.config.getString(kAppSecret);
            } else {
                throw new InstantiationException(
                        "At least " + kAppKey + " and " + kAppSecret + " have to be set in the config. "
                                + kAccessTokenValue + " and " + kAccessTokenSecret + " are optional!");
            }
        } catch (MissingConfigValueException e) {
            e.printStackTrace();
            throw new InstantiationException(e.getMessage());
        }
        return this;
    }

    @Override
    public boolean delete(String resource) {
        boolean ret = performDelete(resource, String.valueOf(this.id));
        if (ret) {
            if (!performDelete(resource, "m")) {
                System.err.println(
                        "The data file has been removed. But unfortunately the meta data file has not been removed!");
            }
        }
        return ret;
    }

    @Override
    public void disconnect() {
    }

    @Override
    public InputStream get(String resource) {
        Response response = performGet(resource, String.valueOf(this.id));
        if (response == null) {
            return null;
        }
        return response.getStream();
    }

    @Override
    public byte[] getMetadata(String resource, int size) {
        Response response = performGet(resource, "m");
        if (response == null) {
            return null;
        }
        BufferedInputStream bis = new BufferedInputStream(response.getStream());
        byte meta[] = new byte[size];
        Arrays.fill(meta, (byte) 0);
        try {
            bis.read(meta, 0, size);
        } catch (IOException ignore) {
            meta = null;
        } finally {
            try {
                bis.close();
            } catch (Exception ignore) {
            }
        }
        return meta;
    }

    /**
     * Restores a file, if the meta data say that that it is deleted
     * ("is_deleted" must be "true"). This must be done to be able to overwrite
     * a deleted file on the Dropbox servers.
     * 
     * @param resource
     *            The resource name.
     * @param extension
     *            The file extension.
     */
    private void maybeRestoreFile(String resource, String extension) {
        OAuthRequest request = new OAuthRequest(GET, DropboxConnector.REVISION_URL + resource + "." + extension);
        this.service.signRequest(this.accessToken, request);
        Response response = request.send();
        if (response.getCode() != 200) {
            return;
        }
        try {
            JSONArray jsonarray = new JSONArray(response.getBody());
            for (int i = 0; i < jsonarray.length(); i++) {
                JSONObject jsonbody = jsonarray.getJSONObject(i);
                if (!jsonbody.optBoolean("is_deleted")) {
                    if (i != 0) {
                        String rev = jsonbody.getString("rev");
                        request = new OAuthRequest(POST,
                                DropboxConnector.RESTORE_URL + resource + "." + extension + "?rev=" + rev);
                        this.service.signRequest(this.accessToken, request);
                        response = request.send();
                    }
                    break;
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Deletes a file from the Dropbox servers.
     * 
     * @param resource
     *            The hash of the file
     * @return true, if the file could be deleted; false, if not.
     */
    private boolean performDelete(String resource, String extension) {
        System.out.println("DELETE " + resource + "." + extension);
        // This request has to be sent as "POST" not as "DELETE"
        OAuthRequest request = new OAuthRequest(POST, DropboxConnector.DELETE_URL + resource + "." + extension);
        this.service.signRequest(this.accessToken, request);
        Response response = request.send();
        System.out.println(response.getCode() + " " + response.getBody());
        if (response.getCode() == 406) {
            System.err.println("Would delete too many files");
            return false;
        }
        return true;
    }

    /**
     * Gets a file from the Dropbox servers.
     * 
     * @param resource
     *            The hash in the merge.input.dir
     * @return the {@link Response} of the GET request on success, else null.
     */
    private Response performGet(String resource, String extension) {
        System.out.println("GET " + resource + "." + extension);
        OAuthRequest request = new OAuthRequest(GET, DropboxConnector.GET_URL + resource + "." + extension);
        this.service.signRequest(this.accessToken, request);
        Response response = request.send();
        System.out.println(response.getCode());
        if (response.getCode() == 404) {
            return null;
        }
        return response;
    }

    /**
     * Uploads a file to the Dropbox servers.
     * 
     * @param resource
     *            The hash in the split.output.dir
     * @return true, if the file could be uploaded; false, if not.
     */
    private boolean performUpload(String resource, String extension) {
        File f = new File(this.splitOutputDir + "/" + resource + "." + extension);
        if (!f.exists()) {
            System.err.println("File does not exist.");
            return false;
        } else {
            int maxFilesize;
            try {
                maxFilesize = this.config.getInt("filesize.max", null);
                if (f.length() > maxFilesize) {
                    System.err.println("File too big");
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        byte[] fileBytes = new byte[(int) f.length()];
        InputStream fis;
        try {
            fis = new FileInputStream(f);
            fis.read(fileBytes);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        OAuthRequest request = new OAuthRequest(PUT,
                DropboxConnector.PUT_URL + resource + "." + extension + "?overwrite=true");
        request.addHeader("Content-Type", DropboxConnector.MIME_MAP.getContentType(f));
        this.service.signRequest(this.accessToken, request);
        request.addPayload(fileBytes);
        Response response = request.send();
        System.out.println(response.getCode() + " " + response.getBody());
        if (response.getCode() == 411 || response.getCode() == 400) {
            System.err.println("Could not PUT file to Dropbox.");
            return false;
        }
        return true;
    }

    @Override
    public boolean update(String resource) {
        System.out.println("Update " + resource + "." + this.id);
        OAuthRequest request = new OAuthRequest(GET, DropboxConnector.META_URL + resource + "." + this.id);
        this.service.signRequest(this.accessToken, request);
        Response response = request.send();
        System.out.println(response.getCode());
        boolean ret;
        if (response.getCode() == 404) {
            return false;
        }
        ret = performUpload(resource, String.valueOf(this.id));
        if (ret) {
            System.out.println("Upload (and overwrite) " + resource + ".m");
            // If the upload of the data file succeeded, the meta data file must
            // be uploaded
            ret = performUpload(resource, "m");
            if (!ret) {
                // If the meta data cannot be uploaded we will remove the data
                // file
                delete(resource);
            }
        }
        return ret;
    }

    @Override
    public boolean upload(String resource) {
        System.out.println("Upload " + resource + "." + this.id);
        OAuthRequest request = new OAuthRequest(GET, DropboxConnector.META_URL + resource + "." + this.id);
        this.service.signRequest(this.accessToken, request);
        Response response = request.send();
        System.out.println(response.getCode());
        System.out.println(response.getBody());
        boolean ret = false;
        try {
            if (response.getCode() == 200) {
                JSONObject jsonbody = new JSONObject(response.getBody());
                if (!jsonbody.optBoolean("is_deleted")) {
                    return false;
                }
            }
            if (response.getCode() == 200 || response.getCode() == 404) {
                maybeRestoreFile(resource, String.valueOf(this.id));
                maybeRestoreFile(resource, "m");
                ret = performUpload(resource, String.valueOf(this.id));
                if (ret) {
                    System.out.println("Upload (and overwrite) " + resource + ".m");
                    // If the upload of the data file succeeded, the meta data
                    // file must be uploaded
                    ret = performUpload(resource, "m");
                    if (!ret) {
                        // If the meta data cannot be uploaded we will remove
                        // the data file
                        delete(resource);
                    }
                }
            }
        } catch (JSONException e) {
            ret = false;
        }
        return ret;
    }
}