crow.api.ApiClient.java Source code

Java tutorial

Introduction

Here is the source code for crow.api.ApiClient.java

Source

/*Copyright (C) 2012 Crow Hou (crow_hou@126.com)
    
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 crow.api;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import crow.api.parser.ParseException;
import crow.api.parser.Parser;
import crow.cache.Cache;

/**
 * 
 * @author crow
 * 
 * @param <C>
 *            APIContext,APIContext ?
 * @param <T>
 *            APIRequest,APIRequest ?*
 * @param <R>
 *            APIResponse 
 */
@SuppressWarnings("hiding")
public abstract class ApiClient<C extends ApiContext, R extends ApiResponse, T extends ApiRequest<? extends R, C>> {
    private static final int SEND_MESSAGE = 1;

    /** Api ? ?? milliseconds */
    public int API_CONNECT_TIMEOUT = 15 * 1000;
    /** Api ? ?? milliseconds */
    public int API_SOCKET_TIMEOUT = 30 * 1000;
    /** ?????  UTF-8 */
    public String PARAM_ENCODING = HTTP.UTF_8;

    private CookieStore cookieStore;
    private HttpContext localContext;
    private Parser parser; // ??
    private ThreadPoolExecutor threadPool;
    private Cache cache;
    private Handler handler;

    // ?
    private Map<T, Runnable> runnableSet = new HashMap<T, Runnable>();

    public ApiClient(Context context, Parser parser, ThreadPoolExecutor threadPool, Cache cache,
            CookieStore store) {
        this.parser = parser;
        this.threadPool = threadPool;
        this.cache = cache;
        this.handler = new Handler(context.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case SEND_MESSAGE:
                    RequestRunnable<?, ?> r = (RequestRunnable<?, ?>) msg.obj;
                    r.sendMessageToCallback();
                    break;
                }
            }
        };
        // Create a local instance of cookie store
        cookieStore = store;
        // Create local HTTP context
        localContext = new BasicHttpContext();
        // Bind custom cookie store to the local context
        localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
    }

    /**
     * ?Api Api ???
     * 
     * @return ?????
     */
    public abstract C getApiContext();

    /**
     * ? Post? ?ApiPost ?? ???????
     * 
     * @param request
     *            
     * @return Post ?
     */
    protected abstract Map<String, String> makeRequestParam(T request);

    /**
     * ?Http ?
     * 
     * @param request
     *            
     * @return
     */
    protected abstract Header[] makeRequestHeaders(T request);

    /**
     * Api ?  1.? 2.?
     * 
     * @param <Q>
     *            Api 
     * @param request
     *            Api
     * @param callback
     *            
     */
    public final <Q extends R> void request(T request, ApiCallback<Q> callback) {
        if (!runnableSet.containsKey(request)) { // ??
            RequestRunnable<T, Q> r = new RequestRunnable<T, Q>(request, callback, false);
            runnableSet.put(request, r);
            threadPool.execute(r);
        }
    }

    public final <Q extends R> void requestWithDirty(T request, ApiCallback<Q> callback) {
        if (!runnableSet.containsKey(request)) {
            RequestRunnable<T, Q> r = new RequestRunnable<T, Q>(request, callback, true);
            runnableSet.put(request, r);
            threadPool.execute(r);
        }
    }

    /**
     * ?request ?
     * 
     * @param request
     */
    public final void cancel(T request) {
        Runnable r;
        if ((r = runnableSet.remove(request)) != null) {
            threadPool.remove(r); // ??
        }
    }

    /**
     * Api 
     * 
     * @param request
     * @return
     * @throws ApiException
     */
    private final boolean postToCache(T request) throws ApiException {
        // 1.http?
        BasicHttpParams httpParams = new BasicHttpParams();
        // 
        HttpConnectionParams.setConnectionTimeout(httpParams, API_CONNECT_TIMEOUT);
        HttpConnectionParams.setSoTimeout(httpParams, API_SOCKET_TIMEOUT);

        HttpPost httpPost = new HttpPost(request.getRequestURL(getApiContext()));
        httpPost.setHeaders(makeRequestHeaders(request)); // 

        DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);

        //  Request ?
        Map<String, String> param = makeRequestParam(request);

        // ???
        ArrayList<BasicNameValuePair> postData = new ArrayList<BasicNameValuePair>();
        for (Map.Entry<String, String> m : param.entrySet()) {
            postData.add(new BasicNameValuePair(m.getKey(), m.getValue()));
        }

        try {
            // ?UTF-8?
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, PARAM_ENCODING);
            // ?
            httpPost.setEntity(entity);
            // ??
            HttpResponse httpResponse = httpClient.execute(httpPost, localContext);
            // ?
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK
                    || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
                List<Cookie> cookies = httpClient.getCookieStore().getCookies();
                for (Cookie c : cookies) {
                    cookieStore.addCookie(c);
                }
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream input = httpEntity.getContent();
                return cache.cache(input, request);
            } else {
                throw new ApiException("Api HttpStatusCode:" + httpResponse.getStatusLine().getStatusCode());
            }
        } catch (Exception e) {
            throw new ApiException("Api ?", e);
        }
    }

    /**
     * Api 
     * 
     * @param request
     * @return
     * @throws ApiException
     */
    private final boolean getToCache(T request) throws ApiException {
        // 1.http?
        BasicHttpParams httpParams = new BasicHttpParams();
        // 
        HttpConnectionParams.setConnectionTimeout(httpParams, API_CONNECT_TIMEOUT);
        HttpConnectionParams.setSoTimeout(httpParams, API_SOCKET_TIMEOUT);

        //  Request ?
        Map<String, String> param = makeRequestParam(request);
        StringBuilder basicUrl = new StringBuilder(request.getRequestURL(getApiContext()));
        basicUrl.append("?");
        // ???
        for (Map.Entry<String, String> m : param.entrySet()) {
            basicUrl.append(m.getKey()).append("=").append(m.getValue()).append("&");
        }

        HttpGet httpGet = new HttpGet(basicUrl.toString());
        httpGet.setHeaders(makeRequestHeaders(request));
        DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);
        try {
            // ??
            HttpResponse httpResponse = httpClient.execute(httpGet);
            // ?
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK
                    || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
                List<Cookie> cookies = httpClient.getCookieStore().getCookies();
                for (Cookie c : cookies) {
                    cookieStore.addCookie(c);
                }
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream input = httpEntity.getContent();
                return cache.cache(input, request);
            } else {
                throw new ApiException("Api HttpStatusCode:" + httpResponse.getStatusLine().getStatusCode());
            }
        } catch (Exception e) {
            throw new ApiException("Api ?", e);
        }
    }

    private class RequestRunnable<P extends T, Q extends R> implements Runnable {
        private P request;
        private ApiCallback<Q> callback;
        private boolean readDirty;
        private Q response;
        private ApiException e;

        public RequestRunnable(P request, ApiCallback<Q> callback, boolean readDirty) {
            this.request = request;
            this.callback = callback;
            this.readDirty = readDirty;
        }

        public void sendMessageToCallback() {
            runnableSet.remove(request); // runnable
            if (response != null) { // 
                if (response.isSuccess()) {
                    callback.onSuccess(response);
                } else {
                    callback.onFail(response);
                }
            } else { // ?
                callback.onException(e);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            if (!cache.isExpired(request)) { // 
                try {
                    response = (Q) parser.parse(cache.getCacheFile(request), request.getResponseClass()); // ?
                    handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this));
                    return;
                } catch (ParseException e) {
                    // do nothing
                }
            }

            // ??
            try {
                if (request.getHttpRequestClass().equals(HttpPost.class)) {
                    if (!postToCache(request)) { // ??
                        throw new ApiException("" + request.toString());
                    }
                } else if (request.getHttpRequestClass().equals(HttpGet.class)) {
                    if (!getToCache(request)) { // ??
                        throw new ApiException("" + request.toString());
                    }
                } else {
                    throw new ApiException(
                            "HttpApiRequestgetHttpRequestClass()?HttpPost.classHttpGet.class"
                                    + request.toString());
                }
            } catch (ApiException e) {
                this.e = e;
                if (!readDirty) { // ??
                    handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this));
                    return;
                }
            }

            try {
                response = (Q) parser.parse(cache.getCacheFile(request), request.getResponseClass());
                if (!response.isSuccess()) { // ?
                    cache.clearCache(request);
                }
            } catch (ParseException e) {
                this.e = new ApiException("??", e);
            }
            handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this));
        }
    }
}