org.lol.reddit.cache.CacheDownload.java Source code

Java tutorial

Introduction

Here is the source code for org.lol.reddit.cache.CacheDownload.java

Source

/*******************************************************************************
 * This file is part of RedReader.
 *
 * RedReader is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * RedReader is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RedReader.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package org.lol.reddit.cache;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.RedirectException;
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.methods.HttpRequestBase;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.lol.reddit.activities.BugReportActivity;
import org.lol.reddit.common.PrioritisedCachedThreadPool;
import org.lol.reddit.common.RRTime;
import org.lol.reddit.jsonwrap.JsonValue;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.UUID;

public final class CacheDownload extends PrioritisedCachedThreadPool.Task {

    private final CacheRequest mInitiator;
    private final CacheManager manager;
    private final UUID session;

    private volatile boolean mCancelled = false;
    private final HttpRequestBase mHttpRequest;

    private final PrioritisedDownloadQueue mQueue;

    public CacheDownload(final CacheRequest initiator, final CacheManager manager,
            final PrioritisedDownloadQueue queue) {

        this.mInitiator = initiator;

        this.manager = manager;
        this.mQueue = queue;

        if (!initiator.setDownload(this)) {
            cancel();
        }

        if (initiator.requestSession != null) {
            session = initiator.requestSession;
        } else {
            session = UUID.randomUUID();
        }

        if (mInitiator.postFields != null) {
            final HttpPost httpPost = new HttpPost(mInitiator.url);
            mHttpRequest = httpPost;
            try {
                httpPost.setEntity(new UrlEncodedFormEntity(mInitiator.postFields, HTTP.UTF_8));
            } catch (UnsupportedEncodingException e) {
                BugReportActivity.handleGlobalError(initiator.context, e);
            }

        } else {
            mHttpRequest = new HttpGet(mInitiator.url);
        }
    }

    public synchronized void cancel() {

        mCancelled = true;

        new Thread() {
            public void run() {
                mHttpRequest.abort();
                mInitiator.notifyFailure(RequestFailureType.CANCELLED, null, null, "Cancelled");
            }
        }.start();
    }

    public void doDownload() {

        if (mCancelled) {
            return;
        }

        try {
            mInitiator.notifyDownloadStarted();
            performDownload(mQueue.getHttpClient(), mHttpRequest);

        } catch (Throwable t) {
            BugReportActivity.handleGlobalError(mInitiator.context, t);
        }
    }

    private void performDownload(final HttpClient httpClient, final HttpRequestBase httpRequest) {

        if (mInitiator.isJson)
            httpRequest.setHeader("Accept-Encoding", "gzip");

        final HttpContext localContext = new BasicHttpContext();
        localContext.setAttribute(ClientContext.COOKIE_STORE, mInitiator.getCookies());

        final HttpResponse response;
        final StatusLine status;

        try {
            if (mCancelled) {
                mInitiator.notifyFailure(RequestFailureType.CANCELLED, null, null, "Cancelled");
                return;
            }
            response = httpClient.execute(httpRequest, localContext);
            status = response.getStatusLine();

        } catch (Throwable t) {

            if (t.getCause() != null && t.getCause() instanceof RedirectException
                    && httpRequest.getURI().getHost().endsWith("reddit.com")) {

                mInitiator.notifyFailure(RequestFailureType.REDDIT_REDIRECT, t, null,
                        "Unable to open a connection");
            } else {
                mInitiator.notifyFailure(RequestFailureType.CONNECTION, t, null, "Unable to open a connection");
            }
            return;
        }

        if (status.getStatusCode() != 200) {
            mInitiator.notifyFailure(RequestFailureType.REQUEST, null, status,
                    String.format("HTTP error %d (%s)", status.getStatusCode(), status.getReasonPhrase()));
            return;
        }

        if (mCancelled) {
            mInitiator.notifyFailure(RequestFailureType.CANCELLED, null, null, "Cancelled");
            return;
        }

        final HttpEntity entity = response.getEntity();

        if (entity == null) {
            mInitiator.notifyFailure(RequestFailureType.CONNECTION, null, status,
                    "Did not receive a valid HTTP response");
            return;
        }

        final InputStream is;

        final String mimetype;
        try {
            is = entity.getContent();
            mimetype = entity.getContentType() == null ? null : entity.getContentType().getValue();
        } catch (Throwable t) {
            t.printStackTrace();
            mInitiator.notifyFailure(RequestFailureType.CONNECTION, t, status, "Could not open an input stream");
            return;
        }

        final NotifyOutputStream cacheOs;
        final CacheManager.WritableCacheFile cacheFile;
        if (mInitiator.cache) {
            try {
                cacheFile = manager.openNewCacheFile(mInitiator, session, mimetype);
                cacheOs = cacheFile.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
                mInitiator.notifyFailure(RequestFailureType.STORAGE, e, null, "Could not access the local cache");
                return;
            }
        } else {
            cacheOs = null;
            cacheFile = null;
        }

        final long contentLength = entity.getContentLength();

        if (mInitiator.isJson) {

            final InputStream bis;

            if (mInitiator.cache) {

                bis = new BufferedInputStream(
                        new CachingInputStream(is, cacheOs, new CachingInputStream.BytesReadListener() {
                            public void onBytesRead(final long total) {
                                mInitiator.notifyProgress(total, contentLength);
                            }
                        }), 8 * 1024);

            } else {
                bis = new BufferedInputStream(is, 8 * 1024);
            }

            final JsonValue value;

            try {
                value = new JsonValue(bis);

                synchronized (this) {
                    mInitiator.notifyJsonParseStarted(value, RRTime.utcCurrentTimeMillis(), session, false);
                }

                value.buildInThisThread();

            } catch (Throwable t) {
                t.printStackTrace();
                mInitiator.notifyFailure(RequestFailureType.PARSE, t, null, "Error parsing the JSON stream");
                return;
            }

            if (mInitiator.cache && cacheFile != null) {
                try {
                    mInitiator.notifySuccess(cacheFile.getReadableCacheFile(), RRTime.utcCurrentTimeMillis(),
                            session, false, mimetype);
                } catch (IOException e) {
                    if (e.getMessage().contains("ENOSPC")) {
                        mInitiator.notifyFailure(RequestFailureType.DISK_SPACE, e, null, "Out of disk space");
                    } else {
                        mInitiator.notifyFailure(RequestFailureType.STORAGE, e, null, "Cache file not found");
                    }
                }
            }

        } else {

            if (!mInitiator.cache) {
                BugReportActivity.handleGlobalError(mInitiator.context, "Cache disabled for non-JSON request");
                return;
            }

            try {
                final byte[] buf = new byte[8 * 1024];

                int bytesRead;
                long totalBytesRead = 0;
                while ((bytesRead = is.read(buf)) > 0) {
                    totalBytesRead += bytesRead;
                    cacheOs.write(buf, 0, bytesRead);
                    mInitiator.notifyProgress(totalBytesRead, contentLength);
                }

                cacheOs.flush();
                cacheOs.close();

                try {
                    mInitiator.notifySuccess(cacheFile.getReadableCacheFile(), RRTime.utcCurrentTimeMillis(),
                            session, false, mimetype);
                } catch (IOException e) {
                    if (e.getMessage().contains("ENOSPC")) {
                        mInitiator.notifyFailure(RequestFailureType.DISK_SPACE, e, null, "Out of disk space");
                    } else {
                        mInitiator.notifyFailure(RequestFailureType.STORAGE, e, null, "Cache file not found");
                    }
                }

            } catch (IOException e) {

                if (e.getMessage() != null && e.getMessage().contains("ENOSPC")) {
                    mInitiator.notifyFailure(RequestFailureType.STORAGE, e, null, "Out of disk space");

                } else {
                    e.printStackTrace();
                    mInitiator.notifyFailure(RequestFailureType.CONNECTION, e, null,
                            "The connection was interrupted");
                }

            } catch (Throwable t) {
                t.printStackTrace();
                mInitiator.notifyFailure(RequestFailureType.CONNECTION, t, null, "The connection was interrupted");
            }
        }
    }

    @Override
    public int getPrimaryPriority() {
        return mInitiator.priority;
    }

    @Override
    public int getSecondaryPriority() {
        return mInitiator.listId;
    }

    @Override
    public void run() {
        doDownload();
    }
}