com.exzogeni.dk.http.HttpTask.java Source code

Java tutorial

Introduction

Here is the source code for com.exzogeni.dk.http.HttpTask.java

Source

/*
 * Copyright (c) 2012-2014 Daniel Serdyukov
 *
 * 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 com.exzogeni.dk.http;

import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.exzogeni.dk.http.cache.CacheManager;
import com.exzogeni.dk.http.cache.CachePolicy;
import com.exzogeni.dk.http.callback.HttpCallback;
import com.exzogeni.dk.log.Logger;

import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.CookieManager;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Daniel Serdyukov
 */
public abstract class HttpTask<V> implements Callable<V> {

    private static final AtomicInteger SEQUENCE = new AtomicInteger();

    private final AtomicInteger mStatusCode = new AtomicInteger();

    private final int mSequence = SEQUENCE.incrementAndGet();

    private final Map<String, List<String>> mHeaders = new ConcurrentHashMap<>();

    private final String mUrl;

    private HttpManager mHttpManager;

    private HttpCallback<V> mCallback;

    private CachePolicy mCachePolicy;

    private int mTimeoutMs;

    private String mEncodedUrl;

    private URI mEncodedUri;

    protected HttpTask(@NonNull String url) {
        this(url, CachePolicy.DEFAULT);
    }

    protected HttpTask(@NonNull String url, @NonNull CachePolicy policy) {
        mUrl = url;
        mCachePolicy = policy;
    }

    @NonNull
    private static Map<String, List<String>> getHeaderFields(HttpURLConnection cn) {
        final Map<String, List<String>> headers = cn.getHeaderFields();
        if (headers != null) {
            final Map<String, List<String>> localHeaders = new HashMap<>(headers);
            localHeaders.remove(null);
            return Collections.unmodifiableMap(localHeaders);
        }
        return Collections.emptyMap();
    }

    @NonNull
    private static InputStream getInputStream(HttpURLConnection cn) {
        try {
            return cn.getInputStream();
        } catch (IOException e) {
            final InputStream stream = cn.getErrorStream();
            if (stream == null) {
                return new ByteArrayInputStream(new byte[0]);
            }
            return stream;
        }
    }

    @NonNull
    public String getUrl() {
        return mUrl;
    }

    @NonNull
    public HttpTask<V> setCallback(@Nullable HttpCallback<V> callback) {
        mCallback = callback;
        return this;
    }

    @NonNull
    public HttpTask<V> setTimeoutMs(int timeoutMs) {
        mTimeoutMs = timeoutMs;
        return this;
    }

    @NonNull
    public HttpTask<V> addHeader(@NonNull String key, @NonNull String... values) {
        List<String> headers = mHeaders.get(key);
        if (headers == null) {
            headers = new CopyOnWriteArrayList<>();
            mHeaders.put(key, headers);
        }
        headers.clear();
        Collections.addAll(headers, values);
        return this;
    }

    @NonNull
    public HttpTask<V> setCachePolicy(@NonNull CachePolicy policy) {
        mCachePolicy = policy;
        return this;
    }

    @NonNull
    public Future<V> submit() {
        return mHttpManager.submit(this);
    }

    @Override
    public V call() throws Exception {
        final long startTime = SystemClock.uptimeMillis();
        try {
            final URI uri = getEncodedUriInternal();
            if (mCachePolicy == null || mCachePolicy.shouldCache(uri)) {
                final CacheManager cm = mHttpManager.getCacheManager();
                final Map<String, List<String>> headers = new HashMap<>();
                final InputStream content = cm.get(uri, headers);
                if (content != null) {
                    return onSuccessInternal(HttpURLConnection.HTTP_NOT_MODIFIED,
                            Collections.<String, List<String>>emptyMap(), content);
                } else {
                    mHeaders.putAll(headers);
                }
            }
            return onPerformNetworkRequest(uri);
        } finally {
            mHttpManager.log(this, (SystemClock.uptimeMillis() - startTime),
                    HttpStatus.getStatusLine(mStatusCode.get()));
        }
    }

    @Override
    public String toString() {
        return "[" + mSequence + "] " + getMethodName() + " " + getEncodedUrlInternal();
    }

    @NonNull
    protected abstract String getMethodName();

    @NonNull
    protected abstract String getEncodedUrl();

    protected void onPrepareConnection(@NonNull HttpURLConnection cn) throws Exception {

    }

    protected void onPerformRequest(@NonNull HttpURLConnection cn) throws Exception {

    }

    void setHttpManager(@NonNull HttpManager manager) {
        mHttpManager = manager;
    }

    void addHeaders(@NonNull Map<String, List<String>> headers) {
        mHeaders.putAll(headers);
    }

    private HttpException onException(HttpException e) {
        if (mCallback != null) {
            mCallback.onException(e);
        }
        return e;
    }

    @NonNull
    private String getEncodedUrlInternal() {
        if (TextUtils.isEmpty(mEncodedUrl)) {
            mEncodedUrl = getEncodedUrl();
        }
        return mEncodedUrl;
    }

    @NonNull
    private URI getEncodedUriInternal() {
        if (mEncodedUri == null) {
            mEncodedUri = URI.create(getEncodedUrlInternal());
        }
        return mEncodedUri;
    }

    private V onPerformNetworkRequest(URI uri) throws Exception {
        Logger.debug("%s", uri);
        final HttpURLConnection cn = (HttpURLConnection) uri.toURL().openConnection();
        try {
            onPrepareConnectionInternal(cn);
            onPerformRequest(cn);
            return onSuccessInternal(cn);
        } finally {
            cn.disconnect();
        }
    }

    private void onPrepareConnectionInternal(HttpURLConnection cn) throws Exception {
        final URI uri = cn.getURL().toURI();
        cn.setRequestMethod(getMethodName());
        cn.setConnectTimeout(mTimeoutMs);
        cn.setReadTimeout(mTimeoutMs);
        final CookieManager cm = mHttpManager.getCookieManager();
        final Map<String, List<String>> cookies = cm.get(uri, new HashMap<String, List<String>>());
        for (final Map.Entry<String, List<String>> cookie : cookies.entrySet()) {
            for (final String value : cookie.getValue()) {
                cn.addRequestProperty(cookie.getKey(), value);
            }
        }
        for (final Map.Entry<String, List<String>> header : mHeaders.entrySet()) {
            for (final String value : header.getValue()) {
                cn.addRequestProperty(header.getKey(), value);
            }
        }
        onPrepareConnection(cn);
    }

    @SuppressWarnings("checkstyle:illegalcatch")
    private V onSuccessInternal(HttpURLConnection cn) throws Exception {
        try {
            final URI uri = getEncodedUriInternal();
            final Map<String, List<String>> headers = getHeaderFields(cn);
            final CookieManager cookieManager = mHttpManager.getCookieManager();
            final CacheManager cacheManager = mHttpManager.getCacheManager();
            cookieManager.put(uri, headers);
            final int responseCode = cn.getResponseCode();
            InputStream content = getInputStream(cn);
            if (responseCode == HttpURLConnection.HTTP_OK && mCachePolicy.shouldCache(uri)) {
                content = cacheManager.put(uri, headers, content);
            } else if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED && mCachePolicy.shouldCache(uri)) {
                content = cacheManager.update(uri, headers);
            }
            return onSuccessInternal(responseCode, headers, content);
        } catch (Exception e) {
            throw onException(new HttpException(getEncodedUrlInternal(), e));
        }
    }

    private V onSuccessInternal(int statusCode, @NonNull Map<String, List<String>> headers,
            @NonNull InputStream content) throws Exception {
        mStatusCode.compareAndSet(mStatusCode.get(), statusCode);
        try {
            if (mCallback != null) {
                return mCallback.onSuccess(mStatusCode.get(), headers, content);
            }
        } finally {
            IOUtils.closeQuietly(content);
        }
        return null;
    }

}