pl.openrnd.connection.rest.ConnectionHandler.java Source code

Java tutorial

Introduction

Here is the source code for pl.openrnd.connection.rest.ConnectionHandler.java

Source

/******************************************************************************
 *
 *  2015 (C) Copyright Open-RnD Sp. z o.o.
 *
 *  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 pl.openrnd.connection.rest;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.loopj.android.http.PersistentCookieStore;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

import pl.openrnd.connection.rest.exception.UnsupportedResponseException;
import pl.openrnd.connection.rest.request.Request;
import pl.openrnd.connection.rest.response.Response;

/**
 * Class that handles requests.
 */
public class ConnectionHandler {

    private static final String TAG = ConnectionHandler.class.getSimpleName();
    private static int mRequestCounter = 0;

    private OnRequestConnectionListener mOnRequestConnectionListener;
    private HttpClient mHttpClient = null;
    private ConnectionLogger mConnectionLogger;

    private ConnectionConfig mConnectionConfig;
    private PersistentCookieStore mCookieStore;
    private HttpContext mHttpContext;

    private Object mClientLock = new Object();
    private Object mCookieLock = new Object();

    private Context mApplicationContext;

    /**
     * Class constructor.
     *
     * @param context Application context.
     * @param connectionConfig ConnectionConfig object with configuration data.
     */
    public ConnectionHandler(Context context, ConnectionConfig connectionConfig) {
        Log.d(TAG, "ConnectionHandler()");

        mApplicationContext = context.getApplicationContext();

        mConnectionConfig = connectionConfig;
        mConnectionLogger = new ConnectionLogger(connectionConfig.getInitialLogsState(),
                connectionConfig.getInitialLogsSize());
    }

    /**
     * Sets OnRequestConnectionListener.
     *
     * @see pl.openrnd.connection.rest.OnRequestConnectionListener
     *
     * @param listener OnRequestConnectionListener object.
     */
    public void setRequestConnectionListener(OnRequestConnectionListener listener) {
        mOnRequestConnectionListener = listener;
    }

    /**
     * Gets ConnectionLogger related to the handler.
     *
     * @see pl.openrnd.connection.rest.ConnectionLogger
     *
     * @return ConnectionLogger object.
     */
    public ConnectionLogger getConnectionLogger() {
        return mConnectionLogger;
    }

    /**
     * Method for clearing cookies
     */
    public void clearCookie() {
        synchronized (mCookieLock) {
            mCookieStore.clear();
            mCookieStore = null;
            mHttpContext = null;
        }
    }

    /**
     * Handles provided request and returns response related to it.
     *
     * The method creates a log if logging mechanism is enabled.
     *
     * Request is executed in a caller thread.
     *
     * @param request Request object
     * @return Response object
     */
    public Response handleRequest(Request request) {
        Response result = null;

        int requestNumber = mRequestCounter++;
        Timer timer = null;

        HttpEntity httpEntity = null;
        InputStream inputStream = null;
        RestConnectionLog.Builder builder = null;
        if (mConnectionLogger.areLogsEnabled()) {
            builder = new RestConnectionLog.Builder();
            builder.request(request);
        }

        Log.d(TAG,
                String.format("handleRequest(%d): ---> [%s]", requestNumber, request.getClass().getSimpleName()));

        try {
            HttpUriRequest httpUriRequest = request.getHttpUriRequest();

            Log.d(TAG,
                    String.format("handleRequest(%d): uri[%s]", requestNumber, httpUriRequest.getURI().toString()));

            logHeaders(requestNumber, httpUriRequest.getAllHeaders());

            HttpResponse httpResponse;

            timer = startRequestTimer(request);
            if (builder != null) {
                builder.request(httpUriRequest);

                httpResponse = execute(httpUriRequest, request.getConnectionTimeout(), request.getReadTimeout());

                builder.response(httpResponse);
            } else {
                httpResponse = execute(httpUriRequest, request.getConnectionTimeout(), request.getReadTimeout());
            }
            stopRequestTimer(timer);

            int statusCode = httpResponse.getStatusLine().getStatusCode();
            String reasonPhrase = httpResponse.getStatusLine().getReasonPhrase();

            Log.d(TAG, String.format("handleRequest(%d): http response[%d / %s]", requestNumber, statusCode,
                    reasonPhrase));

            logHeaders(requestNumber, httpResponse.getAllHeaders());

            httpEntity = httpResponse.getEntity();
            inputStream = httpEntity.getContent();

            boolean isStatusCodeSupported = request.supportsAllStatusCodes();

            if (!isStatusCodeSupported) {
                int[] supportedStatusCodes = request.getSupportedStatusCodes();
                for (int supportedStatusCode : supportedStatusCodes) {
                    if (statusCode == supportedStatusCode) {
                        isStatusCodeSupported = true;
                        break;
                    }
                }
            }

            if (isStatusCodeSupported) {
                result = request.getResponse(statusCode, reasonPhrase, httpResponse.getAllHeaders(), inputStream);
            } else {
                throw new UnsupportedResponseException(statusCode, reasonPhrase, httpResponse.getAllHeaders(),
                        inputStream);
            }
        } catch (Exception exc) {
            Log.e(TAG, String.format("handleRequest(%d): ", requestNumber), exc);

            stopRequestTimer(timer);

            result = request.getResponse(exc);
        } finally {
            if (httpEntity != null) {
                try {
                    httpEntity.consumeContent();
                } catch (Exception exc) {
                    Log.e(TAG, String.format("handleRequest(%d): ", requestNumber), exc);
                }
            }

            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception exc) {
                    Log.e(TAG, String.format("handleRequest(%d): ", requestNumber), exc);
                }
            }
        }

        if (builder != null) {
            builder.cookies(mCookieStore);
            builder.response(result);

            mConnectionLogger.addConnectionLog(builder.build());
        }

        Log.d(TAG, String.format("handleRequest(%d): <---", requestNumber));

        return result;
    }

    private void createCookieIfNotSet() {
        synchronized (mCookieLock) {
            if (!hasCookie()) {
                mCookieStore = new PersistentCookieStore(mApplicationContext);
                mHttpContext = new BasicHttpContext();
                mHttpContext.setAttribute(ClientContext.COOKIE_STORE, mCookieStore);
            }
        }
    }

    private boolean hasCookie() {
        synchronized (mCookieLock) {
            return (mCookieStore != null) && (mHttpContext != null);
        }
    }

    private HttpContext getHttpContext() {
        synchronized (mCookieLock) {
            return mHttpContext;
        }
    }

    private HttpClient getHttpClient() {
        synchronized (mClientLock) {
            if ((null == mHttpClient) || mConnectionConfig.isFullAsync()) {
                HttpParams httpParameters = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(httpParameters, mConnectionConfig.getConnectionTimeout());
                HttpConnectionParams.setSoTimeout(httpParameters, mConnectionConfig.getReadTimeout());
                HttpClientParams.setRedirecting(httpParameters, false);

                if (mConnectionConfig.getSchemeRegistry() != null) {
                    ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(httpParameters,
                            mConnectionConfig.getSchemeRegistry());
                    mHttpClient = new DefaultHttpClient(connectionManager, httpParameters);
                } else {
                    mHttpClient = new DefaultHttpClient(httpParameters);
                }
            }

            return mHttpClient;
        }
    }

    private void notifyTakingTooLong(final Request request) {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (mOnRequestConnectionListener != null) {
                    mOnRequestConnectionListener.onRequestTakingTooLong(request);
                }
            }
        });
    }

    private Timer startRequestTimer(final Request request) {
        Timer result = new Timer();
        result.schedule(new TimerTask() {
            @Override
            public void run() {
                notifyTakingTooLong(request);
            }
        }, mConnectionConfig.getRequestWarningTime());
        return result;
    }

    private void stopRequestTimer(Timer timer) {
        if (timer == null) {
            return;
        }
        timer.purge();
        timer.cancel();
    }

    private HttpResponse execute(HttpUriRequest request, Integer connectionTimeout, Integer readTimeout)
            throws ClientProtocolException, IOException {
        HttpResponse response = null;
        HttpClient httpClient = getHttpClient();
        HttpParams params = httpClient.getParams();

        Integer connectionTimeoutOriginal = null;
        Integer readTimeoutOriginal = null;

        if (connectionTimeout != null) {
            connectionTimeoutOriginal = HttpConnectionParams.getConnectionTimeout(params);
            HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
        }

        if (readTimeout != null) {
            readTimeoutOriginal = HttpConnectionParams.getSoTimeout(params);
            HttpConnectionParams.setSoTimeout(params, readTimeout);
        }

        try {
            if (mConnectionConfig.isUsingCookies()) {
                createCookieIfNotSet();
                response = httpClient.execute(request, getHttpContext());
            } else {
                response = httpClient.execute(request);
            }
        } finally {
            if (connectionTimeoutOriginal != null) {
                HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
            }

            if (readTimeoutOriginal != null) {
                HttpConnectionParams.setSoTimeout(params, readTimeoutOriginal);
            }
        }

        return response;
    }

    private void logHeaders(int id, Header[] headers) {
        if (headers != null) {
            for (Header header : headers) {
                Log.w(TAG, String.format("handleRequest(%d):    key[%s], value[%s]", id, header.getName(),
                        header.getValue()));
            }
        }
    }
}