com.domuslink.communication.ApiHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.domuslink.communication.ApiHandler.java

Source

/*
 * domus.Link api handler
 * 
 * Source derived from com.example.android.simplewiktionary example code.
 * 
 * Copyright (C) 2009 The Android Open Source Project
 *
 * 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.
 */
/*
 * domus.Link :: PHP Web-based frontend for Heyu (X10 Home Automation)
 * Copyright (c) 2007, Istvan Hubay Cebrian (istvan.cebrian@domus.link.co.pt)
 * Project's homepage: http://domus.link.co.pt
 * Project's dev. homepage: http://domuslink.googlecode.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 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope's 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, write to the Free Software Foundation, 
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.domuslink.communication;

import com.domuslink.DomusLink.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
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.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Date;
import java.util.List;

/**
 * Helper methods to simplify talking with and parsing responses from a
 * lightweight domus.Link API. Before making any requests, you should call
 * {@link #prepareUserAgent(Context)} to generate a User-Agent string based on
 * your application package name and version.
 */
public class ApiHandler {
    private static final String TAG = "ApiHandler";

    /**
     * Partial URL to use when requesting a specific function
     * in domus.Link. Use {@link String#format(String, Object...)} to insert
     * the desired function and parameters.
     */
    private static final String DOMUSLINKAPI_PARAM = "//%s/api.php/%s/%s";
    private static final String DOMUSLINKAPI_NOPARAM = "//%s/api.php/%s";

    private static final int GET_TYPE = 1;
    private static final int POST_TYPE = 2;

    /**
     * {@link StatusLine} HTTP status code when no server error has occurred.
     */
    private static final int HTTP_STATUS_OK = 200;

    /**
     * Shared buffer used by {@link #getUrlContent(String)} when reading results
     * from an API request.
     */
    private static byte[] sBuffer = new byte[512];

    /**
     * User-agent string to use when making requests. Should be filled using
     * {@link #prepareUserAgent(Context)} before making any other calls.
     */
    private static String sUserAgent = null;

    private static String sPassword = null;
    private static String sHost = null;

    /**
     * Thrown when there were problems contacting the remote API server, either
     * because of a network error, or the server returned a bad status code.
     */
    public static class ApiException extends Exception {
        /**
        * 
        */
        private static final long serialVersionUID = -3033076470970576123L;

        public ApiException(String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
        }

        public ApiException(String detailMessage) {
            super(detailMessage);
        }
    }

    /**
     * Thrown when there were problems parsing the response to an API call,
     * either because the response was empty, or it was malformed.
     */
    public static class ParseException extends Exception {
        /**
        * 
        */
        private static final long serialVersionUID = -7466356578011545491L;

        public ParseException(String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
        }
    }

    /**
     * Prepare the internal User-Agent string for use. This requires a
     * {@link Context} to pull the package name and version number for this
     * application.
     */
    public static void prepareUserAgent(Context context, String thePassword, String theHost) {
        sPassword = thePassword;
        sHost = theHost;
        try {
            // Read package name and version number from manifest
            PackageManager manager = context.getPackageManager();
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            sUserAgent = String.format(context.getString(R.string.template_user_agent), info.packageName,
                    info.versionName);

        } catch (NameNotFoundException e) {
            Log.e(TAG, "Couldn't find package information in PackageManager", e);
        }
    }

    /**
     * Read and return the content for a specific domus.Link function. This makes a
     * lightweight API call, and trims out just the page content returned.
     * Because this call blocks until results are available, it should not be
     * run from a UI thread.
     *
     * @param baseUrl The base url location of domus.Link
     * @param function The function of the domus.Link api to call
     * @param param The extra parameters to supply to the function
     * @return the JSON object.
     * @throws ApiException If any connection or server error occurs.
     * @throws ParseException If there are problems parsing the response.
     */
    public static JSONObject getPageContent(ApiCookieHandler cookieHandler, String function, String param)
            throws ApiException, ParseException {
        return (pageContent(GET_TYPE, sHost, cookieHandler, function, param));
    }

    public static JSONObject postPageContent(ApiCookieHandler cookieHandler, String function, String param)
            throws ApiException, ParseException {
        return (pageContent(POST_TYPE, sHost, cookieHandler, function, param));
    }

    /**
     * Read and return the content for a specific domus.Link function. This makes a
     * lightweight API call, and trims out just the page content returned.
     * Because this call blocks until results are available, it should not be
     * run from a UI thread.
     *
     * @param type The type of call for either GET or POST
     * @param baseUrl The base url location of domus.Link
     * @param function The function of the domus.Link api to call
     * @param param The extra parameters to supply to the function
     * @return the JSON object.
     * @throws ApiException If any connection or server error occurs.
     * @throws ParseException If there are problems parsing the response.
     */

    protected static JSONObject pageContent(int type, String baseUrl, ApiCookieHandler cookieHandler,
            String function, String param) throws ApiException, ParseException {
        String content;
        URI commandURI = null;
        String uriPath;

        if (param == null)
            uriPath = String.format(DOMUSLINKAPI_NOPARAM, sHost, function);
        else
            uriPath = String.format(DOMUSLINKAPI_PARAM, sHost, function, param);

        // Query the API for content
        try {
            commandURI = new URI("http", uriPath, null);
        } catch (Exception e) {
            Log.e(TAG, "build URI: Url components: [" + baseUrl + "] [" + function + "] [" + param
                    + "] and uriPath of [" + uriPath + "]");
            throw new ParseException("Problem parsing for URI", e);
        }

        content = urlContent(type, commandURI, cookieHandler);

        try {
            // Drill into the JSON response to find the content body
            //           Log.d("DomusLink", "ApiHandler: Content <"+content+">");
            JSONObject response = new JSONObject(content);
            return response;
        } catch (JSONException e) {
            throw new ParseException("Problem parsing API response", e);
        }
    }

    /**
     * Pull the raw text content of the given URL. This call blocks until the
     * operation has completed, and is synchronized because it uses a shared
     * buffer {@link #sBuffer}.
     *
     * @param type The type of either a GET or POST for the request
     * @param commandURI The constructed URI for the path
     * @return The raw content returned by the server.
     * @throws ApiException If any connection or server error occurs.
     */
    protected static synchronized String urlContent(int type, URI commandURI, ApiCookieHandler cookieHandler)
            throws ApiException {
        HttpResponse response;
        HttpRequestBase request;

        if (sUserAgent == null) {
            throw new ApiException("User-Agent string must be prepared");
        }

        // Create client and set our specific user-agent string
        DefaultHttpClient client = new DefaultHttpClient();
        UsernamePasswordCredentials creds = new UsernamePasswordCredentials("", sPassword);
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), creds);
        client.setCredentialsProvider(credsProvider);
        CookieStore cookieStore = cookieHandler.getCookieStore();
        if (cookieStore != null) {
            boolean expiredCookies = false;
            Date nowTime = new Date();
            for (Cookie theCookie : cookieStore.getCookies()) {
                if (theCookie.isExpired(nowTime))
                    expiredCookies = true;
            }
            if (!expiredCookies)
                client.setCookieStore(cookieStore);
            else {
                cookieHandler.setCookieStore(null);
                cookieStore = null;
            }
        }

        try {
            if (type == POST_TYPE)
                request = new HttpPost(commandURI);
            else
                request = new HttpGet(commandURI);

            request.setHeader("User-Agent", sUserAgent);
            response = client.execute(request);

            // Check if server response is valid
            StatusLine status = response.getStatusLine();
            if (status.getStatusCode() != HTTP_STATUS_OK) {
                Log.e(TAG,
                        "urlContent: Url issue: " + commandURI.toString() + " with status: " + status.toString());
                throw new ApiException("Invalid response from server: " + status.toString());
            }

            // Pull content stream from response
            HttpEntity entity = response.getEntity();
            InputStream inputStream = entity.getContent();

            ByteArrayOutputStream content = new ByteArrayOutputStream();

            // Read response into a buffered stream
            int readBytes = 0;
            while ((readBytes = inputStream.read(sBuffer)) != -1) {
                content.write(sBuffer, 0, readBytes);
            }

            if (cookieStore == null) {
                List<Cookie> realCookies = client.getCookieStore().getCookies();
                if (!realCookies.isEmpty()) {
                    BasicCookieStore newCookies = new BasicCookieStore();
                    for (int i = 0; i < realCookies.size(); i++) {
                        newCookies.addCookie(realCookies.get(i));
                        //                      Log.d(TAG, "aCookie - " + realCookies.get(i).toString());
                    }
                    cookieHandler.setCookieStore(newCookies);
                }
            }

            // Return result from buffered stream
            return content.toString();
        } catch (IOException e) {
            Log.e(TAG, "urlContent: client execute: " + commandURI.toString());
            throw new ApiException("Problem communicating with API", e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "urlContent: client execute: " + commandURI.toString());
            throw new ApiException("Problem communicating with API", e);
        } finally {
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            client.getConnectionManager().shutdown();
        }
    }
}