org.surveydroid.android.coms.WebClient.java Source code

Java tutorial

Introduction

Here is the source code for org.surveydroid.android.coms.WebClient.java

Source

/*---------------------------------------------------------------------------*
 * Communicates with the SOC Server and notifies incoming survey whenever    *
 * needed.  Adapted from Android Wikitionary example provided with the       *
 * Android SDK.                                                              *
 *---------------------------------------------------------------------------*
 * Copyright 2011 Sema Berkiten, Vladimir Costescu, Henry Liu, Diego Vargas, *
 * Austin Walker, and Tony Xiao                                              *
 *                                                                           *
 * This file is part of Survey Droid.                                        *
 *                                                                           *
 * Survey Droid 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.                                       *
 *                                                                           *
 * Survey Droid 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 Survey Droid.  If not, see <http://www.gnu.org/licenses/>.     *
 *****************************************************************************/
package org.surveydroid.android.coms;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;

import javax.net.ssl.SSLException;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.surveydroid.android.Config;
import org.surveydroid.android.Util;

import android.content.Context;

/**
 * Helper methods to simplify talking with and parsing responses from a
 * SOC Online API.
 *
 * @author Tony Xaio
 * @author Austin Walker
 */
public abstract class WebClient {
    //logging tag
    private static final String TAG = "WebClient";

    //config key used to indicate that we need to use the custom keystore
    //used as an optimization to avoid catching a lot of exceptions
    private static final String USE_PRIVATE_KEYSTORE = "use_private_keystore";

    //status codes
    private static final int HTTP_STATUS_OK = 200;

    /**
     * Thrown when there were problems contacting the remote API server, either
     * because of a network error, or the server returned a bad status code.
     */
    @SuppressWarnings("serial")
    public static class ApiException extends Exception {
        public ApiException(String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
        }

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

    /**
     * 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 url The exact URL to request.
     * @return The raw content returned by the server.
     * @throws ApiException If any connection or server error occurs.
     */
    protected static synchronized String getUrlContent(Context ctxt, String url) throws ApiException {
        return getUrlContent(ctxt, url, true);
    }

    //same as above with recursion counter
    private static synchronized String getUrlContent(Context ctxt, String url, boolean firstCall)
            throws ApiException {
        // Create client and set our specific user-agent string
        HttpClient client = getClient(ctxt);
        HttpGet request = new HttpGet(url);

        try {
            // Execute HTTP Post Request
            HttpResponse response;
            try {
                response = client.execute(request);
            } catch (SSLException e) {
                Util.w(null, TAG, Util.fmt(e));
                //make sure this isn't the recursive call
                if (!firstCall)
                    throw new ApiException("Untrusted certificate!");
                //switch to using the other client
                swapKeyStore(ctxt);
                //the just do a recursive call
                return getUrlContent(ctxt, url, false);
            } catch (SocketException e) {
                throw new ApiException("Network problem", e);
            }

            // Check if server response is valid
            StatusLine status = response.getStatusLine();
            if (status.getStatusCode() != HTTP_STATUS_OK) {
                throw new ApiException("Invalid response from server: " + status.toString());
            }
            return getInputStreamAsString(ctxt, response.getEntity().getContent());
        } catch (IOException e) {
            throw new ApiException("Problem communicating with API", e);
        }
    }

    /**
     * Posts given JSON content to a url
     * 
     * @param ctxt - the current {@link Context} (usually a service)
     * @param url - full url to send content to
     * @param value - json content
     * 
     * @return true on success
     */
    protected static synchronized boolean postJsonToUrl(Context ctxt, String url, String value) {
        return postJsonToUrl(ctxt, url, value, true);
    }

    //same as above with recursion counter
    private static synchronized boolean postJsonToUrl(Context ctxt, String url, String value, boolean firstCall) {
        HttpClient httpclient = getClient(ctxt);
        HttpPost httpPost = new HttpPost(url);

        try {
            StringEntity se = new StringEntity(value);
            se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            httpPost.setEntity(se);

            // Execute HTTP Post Request
            HttpResponse response;
            try {
                response = httpclient.execute(httpPost);
            } catch (SSLException e) {
                //make sure this isn't the recursive call
                if (!firstCall)
                    throw new ApiException("Untrusted certificate");
                //switch to using the other client
                swapKeyStore(ctxt);
                //the just do a recursive call
                return postJsonToUrl(ctxt, url, value, false);
            }

            Util.d(null, TAG, "Content: " + getInputStreamAsString(ctxt, response.getEntity().getContent()));

            StatusLine status = response.getStatusLine();
            if (status.getStatusCode() == HTTP_STATUS_OK)
                return true;
        } catch (Exception e) {
            Util.e(null, TAG, Util.fmt(e));
        }
        return false;
    }

    private static void swapKeyStore(Context ctxt) {
        Config.putSetting(ctxt, USE_PRIVATE_KEYSTORE, !Config.getSetting(ctxt, USE_PRIVATE_KEYSTORE, false));
    }

    private static HttpClient getClient(Context ctxt) {
        if (Config.getSetting(ctxt, USE_PRIVATE_KEYSTORE, false)) {
            return new SDHttpClient(ctxt);
        } else {
            return new DefaultHttpClient();
        }
    }

    private static String getInputStreamAsString(Context ctxt, InputStream is) {
        byte[] sBuffer = new byte[512];
        ByteArrayOutputStream content = new ByteArrayOutputStream();

        // Read response into a buffered stream
        int readBytes = 0;
        try {
            while ((readBytes = is.read(sBuffer)) != -1) {
                content.write(sBuffer, 0, readBytes);
            }
        } catch (IOException e) {
            Util.e(null, TAG, Util.fmt(e));
        }
        return new String(content.toByteArray());
    }
}