io.calq.android.analytics.ApiDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for io.calq.android.analytics.ApiDispatcher.java

Source

/*
 *  Copyright 2014 Calq.io
 *
 *  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 io.calq.android.analytics;

import io.calq.android.ApiException;
import io.calq.android.LocalConfig;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Vector;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * This is the class that actually dispatches API calls to the Calq API server.
 */
public class ApiDispatcher {

    /**
     * Config settings being used.
     */
    protected LocalConfig config;

    /**
     * Creates a new ApiDispatcher for sending QueuedApiCalls to the Calq server.
     * 
     * @param config   The local config settings to use.
     */
    public ApiDispatcher(LocalConfig config) {
        this.config = config;
    }

    /**
     * Dispatches the given API call to the remote Calq server.
     * @param batch             The batch of API calls to dispatch.
     * @throws ApiException 
     * @returns if this was successful.
     */
    public boolean dispatch(Vector<QueuedApiCall> batch) throws ApiException {

        // At some point we should switch from the Apache client to HttpURLConnection on clients that support it.
        //   See: http://android-developers.blogspot.co.uk/2011/09/androids-http-clients.html

        try {
            HttpClient httpclient = new DefaultHttpClient();

            HttpPost post = new HttpPost(getEndpointUrl(batch.firstElement()));
            post.setHeader("Content-type", "application/json");
            post.setEntity(buildPayload(batch));
            HttpResponse response = httpclient.execute(post);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 500) {
                // 500s we want to retry later
                return false;
            } else if (statusCode != HttpStatus.SC_OK) {

                // Try get response for other codes, might have API error in it
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    response.getEntity().writeTo(out);
                    out.close();
                    String responseString = out.toString();

                    JSONObject json = new JSONObject(responseString);
                    if (json.has("error")) {
                        throw (new ApiException(json.getString("error")));
                    } else {
                        throw (new ApiException("Unknown error occured during API call."));
                    }
                }
            }
        } catch (ClientProtocolException e) {
            // Failed, but don't know why. Signal failed for re-queue
            return false;
        } catch (IOException e) {
            // Failed, but don't know why. Signal failed for re-queue
            return false;
        } catch (JSONException e) {
            // Failed, but don't know why. Signal failed for re-queue
            return false;
        }

        // All OK. This call can be removed from local queue
        return true;
    }

    /**
     * Builds a payload based on the batch content.
     */
    private StringEntity buildPayload(Vector<QueuedApiCall> batch) throws UnsupportedEncodingException {
        // Single item?
        if (batch.size() == 1) {
            return new StringEntity(batch.firstElement().getPayload(), "UTF-8");
        } else {
            StringBuilder payload = new StringBuilder();
            payload.append("[");
            for (int n = 0; n < batch.size(); n++) {
                if (n > 0) {
                    payload.append(",");
                }
                QueuedApiCall apiCall = batch.get(n);
                payload.append(apiCall.getPayload());
            }
            payload.append("]");
            return new StringEntity(payload.toString(), "UTF-8");
        }
    }

    /**
     * Gets the endpoint URL to use for the given API call.
     */
    public String getEndpointUrl(QueuedApiCall apiCall) {
        String apiServerUrl = config.getRemoteApiServerUrl();
        return apiServerUrl + (apiServerUrl.endsWith("/") ? "" : "/") + apiCall.endpoint;
    }

}