com.flattr4android.rest.FlattrRestClient.java Source code

Java tutorial

Introduction

Here is the source code for com.flattr4android.rest.FlattrRestClient.java

Source

/* Copyright (c) 2010-2011 Flattr4Android
 *
 * Licensed 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 com.flattr4android.rest;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.xml.sax.SAXException;

import com.flattr4android.rest.demo.SampleThing;

/**
 * <code>FlattrRestClient</code> is the main entry point to the API.
 */
public class FlattrRestClient {

    public static final String DEMO_SAMPLE_THING_ID = "demo_thing";

    public static final String API_PATH_PREFIX = "/rest/0.5/";

    private OAuthConsumer consumer;
    private boolean demoMode = false;
    private Thing demoSampleThing;
    private ThingCache thingCache = new ThingCache();
    private DefaultHttpClient client;

    public FlattrRestClient(OAuthConsumer consumer) {
        this.consumer = consumer;
    }

    public FlattrRestClient(String consumerKey, String consumerSecret, String accessToken, String tokenSecret) {
        consumer = new FlattrOAuthConsumer(consumerKey, consumerSecret);
        consumer.setTokenWithSecret(accessToken, tokenSecret);
    }

    /**
     * @see FlattrRestClient#setDemoMode(boolean)
     */
    public boolean isDemoMode() {
        return demoMode;
    }

    /**
     * Enable or disable the Demo mode.
     * 
     * When enabled, the demo mode has a couple of side effects:
     * <ul>
     * <li>The things are always "clickable" and click is always successful but
     * does <i>nothing</i>. This is useful to demonstrate the flattring of a
     * thing without actually clicking it, and with the ability to click it
     * several times (to repeat the demo as much as needed).</li>
     * <li>The URL stored in the DEMO_SAMPLE_THING_URL variable</li>
     * </ul>
     */
    public void setDemoMode(boolean demoMode) {
        this.demoMode = demoMode;
    }

    public ThingCache getThingCache() {
        return thingCache;
    }

    /**
     * Return the user representing the authenticated user.
     */
    public User getMe()
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            ParserConfigurationException, SAXException, IOException, FlattrRestException {
        return User.buildUser(this, getResourceInputStream(API_PATH_PREFIX + "user/me"));
    }

    /**
     * Return a user by Id.
     */
    public User getUser(int id)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            ParserConfigurationException, SAXException, IOException, FlattrRestException {
        return User.buildUser(this, getResourceInputStream(API_PATH_PREFIX + "user/get/id/" + id));
    }

    /**
     * Return a user by name.
     */
    public User getUser(String userName)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            ParserConfigurationException, SAXException, IOException, FlattrRestException {
        return User.buildUser(this, getResourceInputStream(API_PATH_PREFIX + "user/get/name/" + userName));
    }

    /**
     * Return a thing by its ID.
     * 
     * @deprecated Use {@link FlattrRestClient#getThingById(String)}
     */
    public Thing getThing(String id) throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrRestException, IOException {
        return getThingById(id);
    }

    /**
     * Return a thing by its ID. This method only look in the cache (and
     * potentialy returns the demo thing) and does not perform any network
     * access.
     */
    public Thing getCachedThingById(String id) {
        if (isDemoMode() && id.equals(DEMO_SAMPLE_THING_ID)) {
            if (demoSampleThing == null) {
                demoSampleThing = new SampleThing();
            }
            return demoSampleThing;
        }
        return thingCache.getThingById(id);
    }

    /**
     * Return a thing by its numeric ID.
     */
    public Thing getThingById(int id) throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrRestException, IOException {
        return getThingById(Integer.toString(id));
    }

    /**
     * Return a thing by its numeric or MD5 ID.
     */
    public Thing getThingById(String id) throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrRestException, IOException {
        // First, look in the cache
        Thing thing = getCachedThingById(id);
        // Not found: network to the rescue
        if (thing == null) {
            thing = Thing.buildOneThing(this, getResourceInputStream(API_PATH_PREFIX + "thing/get/id/" + id));
            thingCache.addOrRefheshThing(thing);
        }
        return thing;
    }

    /**
     * Return a thing by its URL.
     */
    public Thing getThingByURL(String url) throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrRestException, IOException {
        return Thing.buildOneThing(this, getResourceInputStream(API_PATH_PREFIX + "thing/get/url/" + url));
    }

    /**
     * Search things.
     * 
     * @see http://developers.flattr.net/doku.php/thing_methods_0.5
     */
    public ArrayList<Thing> browseThings(String... query) throws OAuthMessageSignerException,
            OAuthExpectationFailedException, OAuthCommunicationException, FlattrRestException, IOException {
        String uri = "thing/browse";
        for (int i = 0; i < query.length; i++) {
            uri += query[i];
        }
        return Thing.buildThings(this, getResourceInputStream(API_PATH_PREFIX + uri));
    }

    public void setDemoSampleThing(Thing model) {
        demoSampleThing = new SampleThing(model);
    }

    /**
     * Get the things of a user.
     * 
     * @see FlattrRestClient#browse(String, List, List, List, List)
     */
    public ArrayList<Thing> getUserThings(int userId) throws OAuthMessageSignerException,
            OAuthExpectationFailedException, OAuthCommunicationException, FlattrRestException, IOException {
        return Thing.buildThings(this, getResourceInputStream(API_PATH_PREFIX + "thing/browse/user/" + userId));
    }

    /**
     * Get the things by a single tag.
     * 
     * @see FlattrRestClient#browse(String, List, List, List, List)
     */
    public ArrayList<Thing> getThingsByTag(String tag) throws OAuthMessageSignerException,
            OAuthExpectationFailedException, OAuthCommunicationException, FlattrRestException, IOException {
        return Thing.buildThings(this, getResourceInputStream(API_PATH_PREFIX + "thing/browse/tag/" + tag));
    }

    /**
     * Browse things.
     * 
     * @param searchedText
     *            Text to be present in the thing's title, or <code>null</code>
     *            if nothing is expected.
     * @param tags
     *            Searched tags, or <code>null</code> or empty list if nothing
     *            is expected.
     * @param categories
     *            Searched categories, or <code>null</code> or empty list if
     *            nothing is expected.
     * @param languages
     *            Searched languages, or <code>null</code> or empty list if
     *            nothing is expected.
     * @param users
     *            Searched users, or <code>null</code> or empty list if nothing
     *            is expected.
     * 
     * @see FlattrRestClient#getCategories()
     * @see FlattrRestClient#getLanguages()
     */
    public ArrayList<Thing> browse(String searchedText, List<String> tags, List<String> categories,
            List<String> languages, List<String> users) throws OAuthMessageSignerException,
            OAuthExpectationFailedException, OAuthCommunicationException, FlattrRestException, IOException {

        return Thing.buildThings(this,
                getResourceInputStream(getBrowseURI(searchedText, tags, categories, languages, users)));
    }

    static String getBrowseURI(String searchedText, List<String> tags, List<String> categories,
            List<String> languages, List<String> users) {
        String uri = API_PATH_PREFIX + "thing/browse";
        if (searchedText != null) {
            uri += "/query/" + searchedText;
        }
        uri += getParameterString("tag", tags);
        uri += getParameterString("category", categories);
        uri += getParameterString("language", languages);
        uri += getParameterString("user", users);
        return uri;
    }

    private static String getParameterString(String paramName, List<String> params) {
        if ((params == null) || (params.size() <= 0)) {
            return "";
        }
        StringBuffer result = new StringBuffer("/");
        result.append(paramName);
        result.append("/");
        for (String s : params) {
            result.append(s);
            result.append(",");
        }
        return result.substring(0, result.length() - 1);
    }

    /**
     * Return the list of clicks performed by the authenticated user.
     */
    public ArrayList<Click> getMyClicks() throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrServerResponseException, FlattrRestException, IOException {
        return getMyClicks("");
    }

    /**
     * Return the list of clicks performed during a specific month by the
     * authenticated user.
     * 
     * @param period
     *            The targeted period (format: "yyyymm", eg. "201012").
     */
    public ArrayList<Click> getMyClicks(String period)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            FlattrServerResponseException, FlattrRestException, IOException {
        return Click.buildClicks(this, getResourceInputStream(API_PATH_PREFIX + "user/clicks/period/" + period));
    }

    public ArrayList<Click> getMyClicks(Date startDate, Date stopDate)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            FlattrServerResponseException, FlattrRestException, IOException {
        ArrayList<Click> clicks = new ArrayList<Click>();
        Calendar cal = Calendar.getInstance();
        cal.setTime(startDate);
        int startYear = cal.get(Calendar.YEAR);
        int startMonth = cal.get(Calendar.MONTH);
        int startDay = cal.get(Calendar.DAY_OF_MONTH);

        cal.setTime(stopDate);
        int stopYear = cal.get(Calendar.YEAR);
        int stopMonth = cal.get(Calendar.MONTH);
        int stopDay = cal.get(Calendar.DAY_OF_MONTH);

        int currentYear = startYear;
        int currentMonth = startMonth;
        Calendar currentClickDate = Calendar.getInstance();

        while ((currentYear < stopYear) || ((currentYear == stopYear) && (currentMonth <= stopMonth))) {
            String period = Integer.toString(currentYear * 100 + (currentMonth + 1));
            ArrayList<Click> current = getMyClicks(period);
            // If we are in the first month of the interval,
            // be careful not to add clicks too early in the month.
            boolean checkLow = ((currentYear == startYear) && (currentMonth == startMonth));
            boolean checkHigh = ((currentYear == stopYear) && (currentMonth == stopMonth));
            if ((!checkLow) && (!checkHigh)) {
                // Clicks in the middle of the interval: add all!
                clicks.addAll(current);
            } else {
                // Check one by one
                for (Click click : current) {
                    currentClickDate.setTime(click.getDate());
                    int currentDay = currentClickDate.get(Calendar.DAY_OF_MONTH);
                    if (((!checkLow) || (currentDay >= startDay)) && ((!checkHigh) || (currentDay <= stopDay))) {
                        clicks.add(click);
                    }
                }
            }

            currentMonth++;
            if (currentMonth > Calendar.DECEMBER) {
                currentMonth = Calendar.JANUARY;
                currentYear++;
            }
        }

        return clicks;
    }

    /**
     * Return the list of ongoing subscriptions of the authenticated user.
     */
    public ArrayList<Subscription> getMySubscriptions()
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            FlattrServerResponseException, FlattrRestException, IOException {
        return Subscription.buildSubscriptions(this, getResourceInputStream(API_PATH_PREFIX + "subscription/list"));
    }

    /**
     * Click a thing by its ID.
     */
    public void clickThing(String id)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            ParserConfigurationException, SAXException, IOException, FlattrServerResponseException {
        if (!isDemoMode()) {
            sendRequest(API_PATH_PREFIX + "thing/click/id/" + id);
        }
        // If demo mode is on, click always works
    }

    /**
     * Register a new thing.
     */
    public Thing register(Thing thing) throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, IOException, IllegalStateException, FlattrRestException {
        String content = "<thing>" + "<url>" + thing.getURL() + "</url>" + "<title><![CDATA[" + thing.getTitle()
                + "]]></title>" + "<category>" + thing.getCategoryName() + "</category>" + "<description><![CDATA["
                + thing.getDescription() + "]]></description>" + "<language>" + thing.getLanguage() + "</language>"
                + "<hidden>0</hidden>" + "<tags>";
        for (String tag : thing.getTags()) {
            content += "<tag>" + tag + "</tag>";
        }
        content += "</tags>" + "</thing>";

        HttpResponse response = sendRequest(API_PATH_PREFIX + "thing/register", "POST", content);
        return Thing.buildOneThing(this, (InputStream) response.getEntity().getContent());
    }

    /**
     * Return the supported languages.
     */
    public ArrayList<Language> getLanguages() throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrServerResponseException, FlattrRestException, IOException {
        return Language.buildLanguages(getResourceInputStream(API_PATH_PREFIX + "feed/languages"));
    }

    /**
     * Return the existing thing categories.
     */
    public ArrayList<Category> getCategories() throws OAuthMessageSignerException, OAuthExpectationFailedException,
            OAuthCommunicationException, FlattrServerResponseException, FlattrRestException, IOException {
        return Category.buildCategories(getResourceInputStream(API_PATH_PREFIX + "feed/categories"));
    }

    protected HttpResponse sendRequest(String uri)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            FlattrServerResponseException, IOException {
        return sendRequest(uri, "GET", null);
    }

    protected HttpResponse sendRequest(String uri, String method, String content)
            throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException,
            IOException, FlattrServerResponseException {
        HttpRequestBase req;
        if (method.equals("GET")) {
            req = new HttpGet("http://api.flattr.com" + uri);
        } else {
            req = new HttpPost("http://api.flattr.com" + uri);
            if (content != null) {
                StringEntity body = new StringEntity("data=" + URLEncoder.encode(content, "utf-8"));
                body.setContentType("application/x-www-form-urlencoded");
                ((HttpPost) req).setEntity(body);
            }
        }
        consumer.sign(req);
        HttpResponse resp = getHttpClient().execute(req);
        int reqCode;
        reqCode = resp.getStatusLine().getStatusCode();
        if (reqCode != 200) {
            if (reqCode == 401) {
                throw new AuthenticationException(resp);
            } else {
                throw new FlattrServerResponseException(resp);
            }
        }
        return resp;
    }

    protected InputStream getResourceInputStream(String uri) throws IOException, OAuthMessageSignerException,
            OAuthExpectationFailedException, OAuthCommunicationException, FlattrServerResponseException {
        return (InputStream) sendRequest(uri).getEntity().getContent();
    }

    private HttpClient getHttpClient() {
        if (client == null) {
            client = new DefaultHttpClient();
        }
        return client;
    }

}