com.phodev.http.tools.ConnectionHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.phodev.http.tools.ConnectionHelper.java

Source

package com.phodev.http.tools;

/**
 *
 * Copyright 2009 Jive Software.
 *
 * 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.
 */
import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
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.client.params.CookiePolicy;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
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.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.phodev.http.tools.receivers.ParasiticRequestReceiver;

/**
 * ??
 * 
 * @author sky
 * 
 */
public class ConnectionHelper {
    public static final String TAG = "ConnectionHelper";
    public static final boolean DEBUG = true;
    public static final int CONNECTION_TIMEOUT = 20000;// ms
    public static final int CON_TIME_OUT_MS = CONNECTION_TIMEOUT;
    public static final int SO_TIME_OUT_MS = CONNECTION_TIMEOUT;
    public static final int MAX_CONNECTIONS_PER_HOST = 20;// SDK20
    public static final int MAX_TOTAL_CONNECTIONS = 10;// ??//SDK2
    public static final int CONNETIONS_MAX_IDLE_TIME = 60 * 1000;//
    // 
    public static final int MAX_CORE_POOL_SIZE = 2;// ?
    public static final int KEEP_ALIVE_TIME = 60;// s//?
    final static ThreadPoolExecutor executor = new ThreadPoolExecutor(MAX_CORE_POOL_SIZE, Integer.MAX_VALUE,
            KEEP_ALIVE_TIME, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    public static final String DEFAULT_CHARSET_STR = "UTF-8";
    public static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_CHARSET_STR);
    //
    private HttpClient httpClient;

    private ConnectionHelper() {
        HttpParams httpParams = new BasicHttpParams();
        ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);
        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_CONNECTIONS_PER_HOST);
        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);

        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setUseExpectContinue(httpParams, false);
        SchemeRegistry reg = new SchemeRegistry();
        reg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        reg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(httpParams, reg);
        HttpConnectionParams.setConnectionTimeout(httpParams, CON_TIME_OUT_MS);
        HttpConnectionParams.setSoTimeout(httpParams, SO_TIME_OUT_MS);

        HttpClientParams.setCookiePolicy(httpParams, CookiePolicy.BROWSER_COMPATIBILITY);
        httpClient = new DefaultHttpClient(connectionManager, httpParams);
    }

    private static ConnectionHelper instance;

    public static synchronized ConnectionHelper obtainInstance() {
        if (instance == null) {
            instance = new ConnectionHelper();
        }
        return instance;
    }

    public enum RequestMethod {
        GET, POST,
        /**
         * multipart form data
         */
        POST_WITH_FILE;
    }

    /**
     * Method??
     * 
     * @param url
     * @param requestId
     *            onFinishrequestId
     * @param rr
     */
    public long httpGet(String url, int requestId, RequestReceiver rr) {
        RequestEntity entity = RequestEntity.obtain();
        entity.setUrl(url).setReceiver(rr).setMethod(RequestMethod.GET).setRequestId(requestId);
        return execute(entity);
    }

    public long httpPost(String url, int requestId, List<NameValuePair> postValues, RequestReceiver rr) {
        return httpPost(url, requestId, postValues, DEFAULT_CHARSET_STR, rr);
    }

    /**
     * Post????
     * 
     * @param url
     * @param requestId
     * @param postValues
     * @param charset
     * @param rr
     */
    public long httpPost(String url, int requestId, List<NameValuePair> postValues, String charset,
            RequestReceiver rr) {
        RequestEntity entity = RequestEntity.obtain();
        entity.setUrl(url).setReceiver(rr).setPostEntitiy(postValues, charset).setMethod(RequestMethod.POST)
                .setRequestId(requestId);
        return execute(entity);
    }

    public long httpPost(String url, int requestId, String queryString, RequestReceiver rr) {
        return httpPost(url, requestId, queryString, DEFAULT_CHARSET_STR, rr);
    }

    /**
     * Post????
     * 
     * @param url
     * @param requestId
     * @param queryString
     * @param charset
     * @param rr
     */
    public long httpPost(String url, int requestId, String queryString, String charset, RequestReceiver rr) {
        RequestEntity entity = RequestEntity.obtain();
        entity.setUrl(url);
        entity.setReceiver(rr);
        entity.setPostEntitiy(queryString, charset);
        entity.setMethod(RequestMethod.POST);
        entity.setRequestId(requestId);
        return execute(entity);
    }

    public long httpPost(String url, int requestId, List<NameValuePair> postValues, Map<String, File> files,
            RequestReceiver rr) {
        return httpPost(url, requestId, postValues, DEFAULT_CHARSET_STR, files, rr);
    }

    /**
     * POST?
     * 
     * @param url
     * @param requestId
     * @param postValues
     * @param files
     * @param rr
     */
    public long httpPost(String url, int requestId, List<NameValuePair> postValues, String charset,
            Map<String, File> files, RequestReceiver rr) {
        RequestEntity entity = RequestEntity.obtain();
        entity.setUrl(url);
        entity.setReceiver(rr);
        entity.setPostEntitiy(postValues, charset, files);
        entity.setMethod(RequestMethod.POST_WITH_FILE);
        entity.setRequestId(requestId);
        return execute(entity);
    }

    private Map<Long, RequestEntity> mRequestRecords = new HashMap<Long, RequestEntity>();

    public long execute(RequestEntity entity) {
        ConnectionTask task = obtainConnectionTask(entity);
        synchronized (mRequestRecords) {
            mRequestRecords.put(entity.getRequestHandler(), entity);
        }
        entity.setRequestTaskFuture(executor.submit(task));
        // executor.execute(obtainConnectionTask(entity));
        return entity.getRequestHandler();
    }

    /**
     * ??
     * 
     * @param reqFingerprint
     * @return
     */
    public boolean isReqeustRunning(long reqFingerprint) {
        synchronized (mRequestRecords) {
            RequestEntity re = mRequestRecords.get(reqFingerprint);
            if (re == null) {
                return false;
            }
            return !re.isCanceled();
        }
    }

    public class RequestEntityNotFoundException extends RuntimeException {
        private static final long serialVersionUID = 3742290111879087686L;
        private long reqFingerprint;

        public RequestEntityNotFoundException(long reqFingerprint) {
            this.reqFingerprint = reqFingerprint;
        }

        @Override
        public String toString() {
            return "RequestEntityNotFoundException reqFingerprint=" + reqFingerprint + " reqeust entity not found";
        }

    }

    /**
     * ?
     * 
     * @param reqFingerprint
     * @return
     */
    public boolean cancleRequest(long reqFingerprint) throws RequestEntityNotFoundException {
        RequestEntity request = null;
        synchronized (mRequestRecords) {
            request = mRequestRecords.get(reqFingerprint);
            if (request == null) {
                throw new RequestEntityNotFoundException(reqFingerprint);
            }
            //
            synchronized (request) {
                if (request.isCanceled() && request.isCancelStateSend()) {
                    return true;
                }
                Future<?> future = request.getRequestTaskFuture();
                if (future == null) {
                    return true;
                }
                request.setCanceled(true);
                //
                try {
                    future.cancel(true);
                } catch (Exception e) {
                    e.printStackTrace();
                    return isReqeustRunning(reqFingerprint);
                }
                tryNotifyCanceled(request);
            }
        }
        return true;
    }

    /**
     * ?
     */
    class ConnectionTask implements Runnable {
        RequestEntity rEntity;
        boolean isInterrupted = false;

        public ConnectionTask(RequestEntity entity) {
            rEntity = entity;
        }

        public void setRequestEntity(RequestEntity entity) {
            rEntity = entity;
        }

        @Override
        public void run() {
            HttpRequestBase httpRequest = null;
            int customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
            int statusCode = -1;
            try {
                if (rEntity.getMethod() == RequestMethod.GET) {
                    httpRequest = new HttpGet(rEntity.getUrl());
                } else {
                    // POST/POST_WITH_FILE
                    HttpPost httpPost = new HttpPost(rEntity.getUrl());
                    // ?
                    httpPost.setEntity(rEntity.getPostEntitiy());
                    httpRequest = httpPost;
                }
                HttpConnectionParams.setSoTimeout(httpRequest.getParams(), CONNECTION_TIMEOUT);
                //
                HttpResponse response = httpClient.execute(httpRequest);
                statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == HttpStatus.SC_OK) {
                    rEntity.setRawResponse(EntityUtils.toString(response.getEntity(), rEntity.getCurrentCharset()));
                    customResultCode = RequestReceiver.RESULT_STATE_OK;
                } else {
                    customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
                }
            } catch (ConnectTimeoutException e) {
                e.printStackTrace();
                customResultCode = RequestReceiver.RESULT_STATE_TIME_OUT;
            } catch (ClientProtocolException e) {
                e.printStackTrace();
                customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
            } catch (Exception e) {
                e.printStackTrace();
                customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
            } finally {
                try {
                    httpRequest.abort();
                } catch (Exception e) {
                    // ignore
                }
            }
            // Debug
            if (DEBUG) {
                reportRequestEntity(rEntity, statusCode);
            }
            RequestReceiver rr = rEntity.getRequestReceiver();
            if (rr instanceof ParasiticRequestReceiver) {
                ((ParasiticRequestReceiver) rr).parasiticHandler(rEntity.getResultCode(), rEntity.getRequestId(),
                        rEntity.getTag(), rEntity.getRawResponse());
            }
            synchronized (this) {
                if (!isInterrupted) {
                    synchronized (rEntity) {
                        if (rEntity.isCanceled()) {
                            tryNotifyCanceled(rEntity);
                        } else {
                            // ??
                            rEntity.setResultCode(customResultCode);
                            Message msg = httpHandler.obtainMessage();
                            msg.obj = rEntity;
                            httpHandler.sendMessage(msg);
                        }
                    }
                }
            }
            recycle();
        }

        public void recycle() {
            rEntity = null;
            isInterrupted = false;
            if (connectionTaskList.size() < 6) {
                connectionTaskList.add(this);
            }
        }
    }

    /**
     * ?cancle???
     * 
     * @param re
     */
    private void tryNotifyCanceled(RequestEntity re) {
        if (re.isCanceled() && !re.isCancelStateSend()) {
            re.setCancelStateSend(true);
            // ??
            Message msg = httpHandler.obtainMessage();
            msg.obj = re;
            httpHandler.sendMessage(msg);
        }
    }

    private static Handler httpHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ?UI Thread
            if (msg.obj instanceof RequestEntity) {
                RequestEntity e = (RequestEntity) msg.obj;
                RequestReceiver rr = e.getRequestReceiver();
                if (e.isCanceled()) {
                    rr.onRequestCanceled(e.getRequestId(), e.getTag());
                } else {
                    rr.onResult(e.getResultCode(), e.getRequestId(), e.getTag(), e.getRawResponse());
                }
                //
                e.recycle();
            }
        }
    };
    private final static Vector<ConnectionTask> connectionTaskList = new Vector<ConnectionTask>();

    /**
     * ?
     * 
     * @param entity
     * @return
     */
    private ConnectionTask obtainConnectionTask(RequestEntity entity) {
        if (connectionTaskList.size() <= 0) {
            return new ConnectionTask(entity);
        } else {
            ConnectionTask task = connectionTaskList.remove(0);
            task.setRequestEntity(entity);
            return task;
        }
    }

    /**
     * HttpClient
     */
    public void shutdownConnection() {
        try {
            httpClient.getConnectionManager().shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void tryRelaseConnetions() {
        httpClient.getConnectionManager().closeIdleConnections(CONNETIONS_MAX_IDLE_TIME, TimeUnit.NANOSECONDS);
    }

    @Override
    protected void finalize() throws Throwable {
        shutdownConnection();
    }

    // public interface HttpTask extends Runnable {
    // public void setTask(RequestEntity entity);
    // }

    // just debug
    public void reportRequestEntity(RequestEntity re, int netStateCode) {
        if (re == null) {
            Log.w(TAG, "------>Connection info RequestEntity is:" + re);
            Log.d(TAG, "------>Connection Thread Pool curr size:" + executor.getPoolSize());
        } else {
            Log.d(TAG, "------>Connection info start");
            Log.d(TAG, "------>Connection Thread Pool curr size:" + executor.getPoolSize());
            Log.d(TAG, "------>Url:" + re.getUrl());
            if (netStateCode < 0) {
                Log.w(TAG, "------>Connection thorws Exception");
            } else {
                Log.d(TAG, "------>Connection StatusCode:" + netStateCode + "  custom ResultCode:"
                        + re.getResultCode());
            }
            // if (re.getPostEntitiy() == null) {
            // Log.d(TAG, "------>PostValue if Exist:null");
            // } else {
            // String postContent = null;
            // HttpEntity entity = re.getPostEntitiy();
            // Header header = entity.getContentEncoding();
            // if (header != null && header.getElements() != null) {
            // postContent = header.getElements().toString();
            // }
            // Log.d(TAG, "------>PostValue if Exist:" + postContent);
            // }
            Log.v(TAG, "------>Raw Result:" + re.getRawResponse());
            Log.d(TAG, "------>Connection info end");
        }
    }
}