ch.cyberduck.core.dropbox.client.AbstractHttpDropboxClient.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.dropbox.client.AbstractHttpDropboxClient.java

Source

package ch.cyberduck.core.dropbox.client;

/*
 * Copyright (c) 2002-2010 David Kocher. All rights reserved.
 *
 * http://cyberduck.ch/
 *
 * 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 2 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.
 *
 * Bug fixes, suggestions and comments should be sent to:
 * dkocher@cyberduck.ch
 *
 * Derived from Official Dropbox API client for Java.
 * http://bitbucket.org/dropboxapi/dropbox-client-java
 */

import oauth.signpost.OAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.exception.OAuthException;
import ch.cyberduck.core.ConnectionCanceledException;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public abstract class AbstractHttpDropboxClient {
    private static Logger log = Logger.getLogger(AbstractHttpDropboxClient.class);

    protected Authenticator auth;

    /**
     * Context
     */
    protected static final String ROOT = "dropbox";
    /**
     *
     */
    protected static final int BUFFER_SIZE = 2048;
    /**
     * This is set by Dropbox to indicate what version of the API you are using.
     */
    protected static final int API_VERSION = 0;

    /**
     *
     */
    private String api_host;
    /**
     *
     */
    private String content_host;
    private String protocol;
    private int port;

    protected HttpClient client;

    protected AbstractHttpDropboxClient(HttpClient client, String api_host, String content_host, String protocol,
            int port) {
        this.api_host = api_host;
        this.content_host = content_host;
        this.protocol = protocol;
        this.port = port;
        this.client = client;
        HttpParams params = this.client.getParams();
        HttpProtocolParams.setUseExpectContinue(params, false);
    }

    protected HttpClient getClient() {
        return client;
    }

    /**
     * Used when we have to authenticate from scratch
     *
     * @param username
     * @param password
     * @return
     * @throws IOException
     * @throws OAuthException
     */
    public void authenticate(String key, String secret, String username, String password) throws IOException {
        String[] params = { "email", username, "password", password };

        OAuthConsumer consumer = new CommonsHttpOAuthConsumer(key, secret);
        HttpGet request = new HttpGet(this.getUrl("/token", params, false));
        try {
            consumer.sign(request);
        } catch (OAuthException e) {
            IOException failure = new IOException(e.getMessage());
            failure.initCause(e);
            throw failure;
        }

        JSONObject credentials = this.parse(this.execute(request));

        String token_key = credentials.get("token").toString();
        String token_secret = credentials.get("secret").toString();
        log.info("Obtained Token Key:" + token_key);
        log.info("Obtained Token Secret:" + token_secret);

        auth = new Authenticator(key, secret, this.getRequestPath("/oauth/request_token"),
                this.getRequestPath("/oauth/access_token"), this.getRequestPath("/oauth/authorize"), token_key,
                token_secret);
    }

    /**
     * Used internally to simplify making requests to the services, and handling an error, parsing
     * JSON, or returning a non-JSON body.  See executeRequest to see what's in the returned Map.
     */
    protected HttpResponse request(HttpUriRequest req) throws IOException {
        if (null == auth) {
            throw new ConnectionCanceledException();
        }
        try {
            auth.sign(req);
        } catch (OAuthException e) {
            IOException failure = new IOException(e.getMessage());
            failure.initCause(e);
            throw failure;
        }
        return this.execute(req);
    }

    /**
     * Used internally to URL encode a list of parameters, which makes it easier to do params than with a map.
     * If you want to use this, the params are organized as key=value pairs in a row, and should convert to Strings.
     */
    protected String urlencode(String[] params) {
        String result = "";
        try {
            boolean firstTime = true;
            for (int i = 0; i < params.length; i += 2) {
                if (params[i + 1] != null) {
                    if (firstTime) {
                        firstTime = false;
                    } else {
                        result += "&";
                    }
                    result += URLEncoder.encode("" + params[i], "UTF-8") + "="
                            + URLEncoder.encode("" + params[i + 1], "UTF-8");
                }
            }

        } catch (UnsupportedEncodingException e) {
            // This shouldn't show up, since UTF-8 should always be supported.
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * Used internally to simplify reading a response in buffered lines.
     */
    private String readResponse(HttpResponse response) throws IOException {
        HttpEntity ent = response.getEntity();
        BufferedReader in = new BufferedReader(new InputStreamReader(ent.getContent()), BUFFER_SIZE);
        String inputLine;
        String result = StringUtils.EMPTY;
        while ((inputLine = in.readLine()) != null) {
            result += inputLine;
        }
        return result;
    }

    /**
     * Verify response and consume any open resources.
     *
     * @param response
     * @throws IOException
     */
    protected void finish(HttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        if (null != entity) {
            // Release all allocated resources
            EntityUtils.consume(entity);
        }
        this.verify(response);
    }

    /**
     * Check for 200 response code
     *
     * @param response
     * @throws IOException Throw exception if response status is not OK
     */
    protected void verify(HttpResponse response) throws IOException {
        if (response.getStatusLine().getStatusCode() != 200) {
            throw new IOException(response.getStatusLine().getReasonPhrase());
        }
    }

    /**
     * Parse JSON response.
     *
     * @param response
     * @return
     * @throws IOException
     */
    protected JSONObject parse(HttpResponse response) throws IOException {
        try {
            this.verify(response);
            String body = this.readResponse(response);
            return (JSONObject) new JSONParser().parse(body);
        } catch (ParseException e) {
            throw new IOException("Invalid JSON response from server");
        } finally {
            this.finish(response);
        }
    }

    protected HttpResponse execute(HttpUriRequest req) throws IOException {
        return client.execute(req);
    }

    protected HttpUriRequest buildRequest(String method, String path) {
        return this.buildRequest(method, path, new String[] {});
    }

    /**
     * Used internally to construct a complete URL to a given host, which can sometimes
     * be the "API host" or the "content host" depending on the type of call.
     *
     * @param method
     * @param path
     * @param params
     * @return
     */
    protected HttpUriRequest buildRequest(String method, String path, String[] params) {
        return this.buildRequest(method, path, params, false);
    }

    /**
     * @param method
     * @param path
     * @param content
     * @return
     */
    protected HttpUriRequest buildRequest(String method, String path, boolean content) {
        return this.buildRequest(method, path, new String[] {}, content);
    }

    /**
     * @param method
     * @param path
     * @param params
     * @param content
     * @return
     */
    protected HttpUriRequest buildRequest(String method, String path, String[] params, boolean content) {
        if (method.equals(HttpGet.METHOD_NAME)) {
            return new HttpGet(this.getUrl(path, params, content));
        } else if (method.equals(HttpPost.METHOD_NAME)) {
            HttpPost post = new HttpPost(this.getUrl(path, params, content));
            if (params != null && params.length > 2) {
                List<BasicNameValuePair> form = new ArrayList<BasicNameValuePair>();
                for (int i = 0; i < params.length; i += 2) {
                    if (params[i + 1] != null) {
                        form.add(new BasicNameValuePair("" + params[i], "" + params[i + 1]));
                    }
                }
                try {
                    post.setEntity(new UrlEncodedFormEntity(form, HTTP.UTF_8));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
            return post;
        }
        throw new IllegalArgumentException(method);
    }

    /**
     * API host
     *
     * @param path
     * @return
     */
    protected String getUrl(String path) {
        return this.getUrl(path, new String[] {}, false);
    }

    /**
     * @param path
     * @param content URL for content server
     * @return
     */
    protected String getUrl(String path, String[] params, boolean content) {
        if (content) {
            return protocol + "://" + content_host + ":" + port + this.getRequestPath(path, params);
        }
        return protocol + "://" + api_host + ":" + port + this.getRequestPath(path, params);
    }

    /**
     * No parameters in URL
     *
     * @param target
     * @return
     */
    protected String getRequestPath(String target) {
        return this.getRequestPath(target, new String[] {});
    }

    /**
     * Used internally to build a URL path + params (if given) according to the API_VERSION.
     */
    protected String getRequestPath(String target, String[] params) {
        try {
            // we have to encode the whole line, then remove + and / encoding to get a good oauth url
            target = URLEncoder.encode("/" + API_VERSION + target, "UTF-8");
            target = target.replace("%2F", "/").replace("+", "%20");
            if (params != null && params.length > 0) {
                target += "?" + urlencode(params);
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return target;
    }

    /**
     * Get a file from the content server, returning the raw Apache HTTP Components response object
     * so you can stream it or work with it how you need.
     * You *must* call .getEntity().consumeContent() on the returned HttpResponse object or you might leak
     * connections.
     *
     * @param from_path
     * @return
     * @throws IOException
     */
    protected HttpResponse getFile(String from_path) throws IOException {
        return getFile(from_path, null);
    }

    /**
     * Get a file from the content server, returning the raw Apache HTTP Components response object
     * so you can stream it or work with it how you need.
     * You *must* call .getEntity().consumeContent() on the returned HttpResponse object or you might leak
     * connections.
     *
     * @param path
     * @param etag Version of the file
     * @return
     * @throws IOException
     */
    protected HttpResponse getFile(String path, String etag) throws IOException {
        HttpUriRequest req = this.buildRequest(HttpGet.METHOD_NAME, "/files/" + ROOT + path, true);
        if (StringUtils.isNotBlank(etag)) {
            req.addHeader("If-None-Match", etag);
        }
        return this.request(req);
    }
}