package com.tomclaw.mandarin.core;
import com.tomclaw.mandarin.core.exceptions.DownloadCancelledException;
import com.tomclaw.mandarin.core.exceptions.DownloadException;
import com.tomclaw.mandarin.im.AccountRoot;
import com.tomclaw.mandarin.util.Logger;
import com.tomclaw.mandarin.util.VariableBuffer;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by solkin on 30.10.14.
*/
public abstract class RangedDownloadRequest<A extends AccountRoot> extends Request<A> {
public RangedDownloadRequest() {
}
@Override
public int executeRequest() {
try {
onStarted();
URL url = new URL(getUrl());
long size = getSize();
FileOutputStream outputStream = getOutputStream();
long read = outputStream.getChannel().size();
VariableBuffer buffer = new VariableBuffer();
onDownload();
do {
outputStream.getChannel().position(read);
String range = "bytes=" + read + "-" + (size - 1);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Range", range);
Logger.log("Range: " + range);
try {
int statusCode = connection.getResponseCode();
Logger.log("Server returns " + statusCode);
if (statusCode == 503) {
throw new IllegalStateException();
} else if (statusCode != 200 && statusCode != 206) {
throw new DownloadException();
}
InputStream input = connection.getInputStream();
int cache;
buffer.onExecuteStart();
while ((cache = input.read(buffer.calculateBuffer())) != -1) {
buffer.onExecuteCompleted(cache);
outputStream.write(buffer.getBuffer(), 0, cache);
outputStream.flush();
read += cache;
onBufferReleased(read, size);
buffer.onExecuteStart();
// Checking for thread is interrupted.
if (Thread.interrupted()) {
// Request is interrupted.
throw new DownloadCancelledException();
}
}
} catch (InterruptedIOException ex) {
// Request might be interrupted.
throw new DownloadCancelledException();
} catch (IOException ex) {
// Pretty network exception.
Logger.log("Io exception while downloading", ex);
Thread.sleep(3000);
}
} while (read < size);
onSuccess();
Logger.log("Download completed successfully.");
} catch (DownloadException ex) {
onFail();
Logger.log("Server returned strange error.");
return REQUEST_DELETE;
} catch (InterruptedIOException ex) {
Logger.log("Download interrupted.");
onCancel();
return REQUEST_LATER;
} catch (InterruptedException ex) {
Logger.log("Download interrupted.");
onCancel();
return REQUEST_LATER;
} catch (DownloadCancelledException ex) {
// No need to process task this time.
onCancel();
return REQUEST_LATER;
} catch (FileNotFoundException ex) {
onFileNotFound();
return REQUEST_DELETE;
} catch (IOException ex) {
onPending();
return REQUEST_PENDING;
} catch (IllegalStateException ex) {
Logger.log("Server is temporary unavailable.");
onPending();
return REQUEST_PENDING;
} catch (Throwable ex) {
onPending();
return REQUEST_PENDING;
}
return REQUEST_DELETE;
}
/**
* Returns remote content Url, needs to be downloaded.
*
* @return String - download Url.
*/
public abstract String getUrl() throws Throwable;
/**
* Returns remote content size. It may be resolved right now.
*
* @return long - remote content size.
*/
public abstract long getSize();
/**
* Output stream to store downloaded data.
*
* @return FileOutputStream to store data.
*/
public abstract FileOutputStream getOutputStream() throws FileNotFoundException;
protected abstract void onStarted() throws Throwable;
protected abstract void onDownload();
protected abstract void onBufferReleased(long sent, long size);
protected abstract void onFileNotFound();
protected abstract void onFail();
protected abstract void onCancel();
protected abstract void onSuccess();
protected abstract void onPending();
}