Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
fr.bmartel.speedtest.SpeedTestTask Maven / Gradle / Ivy
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Bertrand Martel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package fr.bmartel.speedtest;
import fr.bmartel.protocol.http.HttpFrame;
import fr.bmartel.protocol.http.states.HttpStates;
import fr.bmartel.speedtest.inter.ISpeedTestListener;
import fr.bmartel.speedtest.inter.ISpeedTestSocket;
import fr.bmartel.speedtest.model.SpeedTestError;
import fr.bmartel.speedtest.model.SpeedTestMode;
import fr.bmartel.speedtest.model.UploadStorageType;
import fr.bmartel.speedtest.utils.RandomGen;
import fr.bmartel.speedtest.utils.SpeedTestUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.concurrent.*;
/**
* This class manage all download/upload operations.
*
* @author Bertrand Martel
*/
public class SpeedTestTask {
/**
* socket server hostname.
*/
private String mHostname = "";
/**
* socket server port.
*/
private int mPort;
/**
* socket object.
*/
private Socket mSocket;
/**
* start time triggered in millis.
*/
private long mTimeStart;
/**
* end time triggered in millis.
*/
private long mTimeEnd;
/**
* this is the number of bit uploaded at this time.
*/
private int mUploadTempFileSize;
/**
* this is the number of packet downloaded at this time.
*/
private int mDownloadTemporaryPacketSize;
/**
* this is the number of packet to download.
*/
private BigDecimal mDownloadPckSize = BigDecimal.ZERO;
/**
* FTP inputstream.
*/
private InputStream mFtpInputstream;
/**
* FTP outputstream.
*/
private OutputStream mFtpOutputstream;
/**
* define if an error has been dispatched already or not. This is reset to false on start download/ upload + in
* reading thread
*/
private boolean mErrorDispatched;
/**
* define if mSocket close error is to be expected.
*/
private boolean mForceCloseSocket;
/**
* size of file to upload.
*/
private BigDecimal mUploadFileSize = BigDecimal.ZERO;
/**
* SpeedTestSocket interface.
*/
private final ISpeedTestSocket mSocketInterface;
/**
* Speed test repeat wrapper.
*/
private final RepeatWrapper mRepeatWrapper;
/**
* Listener list.
*/
private final List mListenerList;
/**
* define if report interval is set.
*/
private boolean mReportInterval;
/**
* executor service for reading operation.
*/
private ExecutorService mReadExecutorService;
/**
* executor service for writing operation.
*/
private ExecutorService mWriteExecutorService;
/**
* executor service used for reporting.
*/
private ScheduledExecutorService mReportExecutorService;
/**
* current speed test mode.
*/
private SpeedTestMode mSpeedTestMode = SpeedTestMode.NONE;
/**
* Build socket.
*
* @param socketInterface interface shared between repeat wrapper and speed test socket
*/
public SpeedTestTask(final ISpeedTestSocket socketInterface, final List listenerList) {
mSocketInterface = socketInterface;
mRepeatWrapper = mSocketInterface.getRepeatWrapper();
mListenerList = listenerList;
initThreadPool();
}
/**
* initialize thread pool.
*/
private void initThreadPool() {
mReadExecutorService = Executors.newSingleThreadExecutor();
mReportExecutorService = Executors.newScheduledThreadPool(SpeedTestConst.THREAD_POOL_REPORT_SIZE);
mWriteExecutorService = Executors.newSingleThreadExecutor();
}
/**
* Set report interval state.
*
* @param state define if a report interval is set
*/
public void setReportInterval(final boolean state) {
mReportInterval = state;
}
/**
* start download task.
*
* @param hostname server mHostname
* @param port server mPort
* @param uri uri to fetch to download file
*/
public void startDownloadRequest(final String hostname, final int port, final String uri) {
mSpeedTestMode = SpeedTestMode.DOWNLOAD;
mForceCloseSocket = false;
mErrorDispatched = false;
this.mHostname = hostname;
this.mPort = port;
final String downloadRequest = "GET " + uri + " HTTP/1.1\r\n" + "Host: " + hostname + "\r\n\r\n";
writeDownload(downloadRequest.getBytes());
}
/**
* shutdown executors to release threads.
*/
private void closeExecutors() {
mReadExecutorService.shutdownNow();
mReportExecutorService.shutdownNow();
mWriteExecutorService.shutdownNow();
}
/**
* Write upload POST request with file generated randomly.
*/
public void writeUpload(final String hostname, final int port, final String uri, final int fileSizeOctet) {
mSpeedTestMode = SpeedTestMode.UPLOAD;
this.mHostname = hostname;
this.mPort = port;
mUploadFileSize = new BigDecimal(fileSizeOctet);
mForceCloseSocket = false;
mErrorDispatched = false;
mUploadTempFileSize = 0;
mTimeStart = System.currentTimeMillis();
connectAndExecuteTask(new Runnable() {
@Override
public void run() {
if (mSocket != null && !mSocket.isClosed()) {
RandomAccessFile uploadFile = null;
final RandomGen randomGen = new RandomGen();
try {
byte[] body = new byte[]{};
if (mSocketInterface.getUploadStorageType() == UploadStorageType.RAM_STORAGE) {
/* generate a file with size of fileSizeOctet octet */
body = randomGen.generateRandomArray(fileSizeOctet);
} else {
uploadFile = randomGen.generateRandomFile(fileSizeOctet);
uploadFile.seek(0);
}
final String head = "POST " + uri + " HTTP/1.1\r\n" + "Host: " + hostname + "\r\nAccept: " +
"*/*\r\nContent-Length: " + fileSizeOctet + "\r\n\r\n";
mUploadTempFileSize = 0;
final int uploadChunkSize = mSocketInterface.getUploadChunkSize();
final int step = fileSizeOctet / uploadChunkSize;
final int remain = fileSizeOctet % uploadChunkSize;
if (mSocket.getOutputStream() != null) {
if (writeFlushSocket(head.getBytes()) != 0) {
throw new SocketTimeoutException();
}
mTimeStart = System.currentTimeMillis();
mTimeEnd = 0;
if (mRepeatWrapper.isFirstUpload()) {
mRepeatWrapper.setFirstUploadRepeat(false);
mRepeatWrapper.setStartDate(mTimeStart);
}
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updatePacketSize(mUploadFileSize);
}
for (int i = 0; i < step; i++) {
final byte[] chunk = SpeedTestUtils.readUploadData(mSocketInterface
.getUploadStorageType(),
body,
uploadFile,
mUploadTempFileSize,
uploadChunkSize);
if (writeFlushSocket(chunk) != 0) {
throw new SocketTimeoutException();
}
mUploadTempFileSize += uploadChunkSize;
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updateTempPacketSize(uploadChunkSize);
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int j = 0; j < mListenerList.size(); j++) {
mListenerList.get(j).onUploadProgress(report.getProgressPercent(), report);
}
}
}
final byte[] chunk = SpeedTestUtils.readUploadData(mSocketInterface.getUploadStorageType(),
body,
uploadFile,
mUploadTempFileSize,
remain);
if (remain != 0 && writeFlushSocket(chunk) != 0) {
throw new SocketTimeoutException();
} else {
mUploadTempFileSize += remain;
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updateTempPacketSize(remain);
}
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int j = 0; j < mListenerList.size(); j++) {
mListenerList.get(j).onUploadProgress(SpeedTestConst.PERCENT_MAX.floatValue(),
report);
}
}
}
} catch (SocketTimeoutException e) {
mReportInterval = false;
mErrorDispatched = true;
closeSocket();
closeExecutors();
if (!mForceCloseSocket) {
SpeedTestUtils.dispatchSocketTimeout(mForceCloseSocket, mListenerList,
false, SpeedTestConst.SOCKET_WRITE_ERROR);
} else {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, e.getMessage());
}
} catch (IOException e) {
mReportInterval = false;
mErrorDispatched = true;
closeExecutors();
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, e.getMessage());
} finally {
if (uploadFile != null) {
try {
uploadFile.close();
randomGen.deleteFile();
} catch (IOException e) {
//e.printStackTrace();
}
}
}
}
}
}, false);
}
/**
* Create and connect mSocket.
*
* @param task task to be executed when connected to mSocket
* @param download define if it is a download or upload test
*/
private void connectAndExecuteTask(final Runnable task, final boolean download) {
// close mSocket before recreating it
if (mSocket != null) {
closeSocket();
}
try {
/* create a basic mSocket connection */
mSocket = new Socket();
if (mSocketInterface.getSocketTimeout() != 0 && download) {
mSocket.setSoTimeout(mSocketInterface.getSocketTimeout());
}
/* establish mSocket parameters */
mSocket.setReuseAddress(true);
mSocket.setKeepAlive(true);
mSocket.connect(new InetSocketAddress(mHostname, mPort));
if (mReadExecutorService == null || mReadExecutorService.isShutdown()) {
mReadExecutorService = Executors.newSingleThreadExecutor();
}
mReadExecutorService.execute(new Runnable() {
@Override
public void run() {
if (download) {
startSocketDownloadTask();
} else {
startSocketUploadTask();
}
mSpeedTestMode = SpeedTestMode.NONE;
}
});
if (mWriteExecutorService == null || mWriteExecutorService.isShutdown()) {
mWriteExecutorService = Executors.newSingleThreadExecutor();
}
mWriteExecutorService.execute(new Runnable() {
@Override
public void run() {
if (task != null) {
task.run();
}
}
});
} catch (IOException e) {
if (!mErrorDispatched) {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, download, e.getMessage());
}
}
}
/**
* start download reading task.
*/
private void startSocketDownloadTask() {
mDownloadTemporaryPacketSize = 0;
try {
final HttpFrame httpFrame = new HttpFrame();
mTimeStart = System.currentTimeMillis();
mTimeEnd = 0;
if (mRepeatWrapper.isFirstDownload()) {
mRepeatWrapper.setFirstDownloadRepeat(false);
mRepeatWrapper.setStartDate(mTimeStart);
}
final HttpStates httFrameState = httpFrame.decodeFrame(mSocket.getInputStream());
SpeedTestUtils.checkHttpFrameError(mForceCloseSocket, mListenerList, httFrameState);
final HttpStates httpHeaderState = httpFrame.parseHeader(mSocket.getInputStream());
SpeedTestUtils.checkHttpHeaderError(mForceCloseSocket, mListenerList, httpHeaderState);
SpeedTestUtils.checkHttpContentLengthError(mForceCloseSocket, mListenerList, httpFrame);
if (httpFrame.getStatusCode() == SpeedTestConst.HTTP_OK &&
httpFrame.getReasonPhrase().equalsIgnoreCase("ok")) {
mDownloadPckSize = new BigDecimal(httpFrame.getContentLength());
if (mRepeatWrapper.isRepeatDownload()) {
mRepeatWrapper.updatePacketSize(mDownloadPckSize);
}
downloadReadingLoop();
mTimeEnd = System.currentTimeMillis();
closeSocket();
mReportInterval = false;
if (!mRepeatWrapper.isRepeatDownload()) {
closeExecutors();
}
final SpeedTestReport report = mSocketInterface.getLiveDownloadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onDownloadFinished(report);
}
} else {
mReportInterval = false;
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onDownloadError(SpeedTestError.INVALID_HTTP_RESPONSE, "Error status code " +
httpFrame.getStatusCode());
}
closeSocket();
if (!mRepeatWrapper.isRepeatDownload()) {
closeExecutors();
}
}
} catch (SocketTimeoutException e) {
mReportInterval = false;
SpeedTestUtils.dispatchSocketTimeout(mForceCloseSocket, mListenerList, true, e.getMessage());
mTimeEnd = System.currentTimeMillis();
closeSocket();
closeExecutors();
} catch (IOException | InterruptedException e) {
mReportInterval = false;
catchError(true, e.getMessage());
}
mErrorDispatched = false;
}
/**
* start download reading loop + monitor progress.
*
* @throws IOException mSocket io exception
*/
private void downloadReadingLoop() throws IOException {
final byte[] buffer = new byte[SpeedTestConst.READ_BUFFER_SIZE];
int read;
while ((read = mSocket.getInputStream().read(buffer)) != -1) {
mDownloadTemporaryPacketSize += read;
if (mRepeatWrapper.isRepeatDownload()) {
mRepeatWrapper.updateTempPacketSize(read);
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveDownloadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onDownloadProgress(report.getProgressPercent(), report);
}
}
if (mDownloadTemporaryPacketSize == mDownloadPckSize.longValueExact()) {
break;
}
}
}
/**
* start upload writing task.
*/
private void startSocketUploadTask() {
try {
final HttpFrame frame = new HttpFrame();
final HttpStates httpStates = frame.parseHttp(mSocket.getInputStream());
if (httpStates == HttpStates.HTTP_FRAME_OK) {
if (frame.getStatusCode() == SpeedTestConst.HTTP_OK && frame.getReasonPhrase().equalsIgnoreCase("ok")) {
mTimeEnd = System.currentTimeMillis();
closeSocket();
mReportInterval = false;
if (!mRepeatWrapper.isRepeatUpload()) {
closeExecutors();
}
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onUploadFinished(report);
}
} else {
mReportInterval = false;
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onUploadError(SpeedTestError.INVALID_HTTP_RESPONSE, "Error status code" +
" " + frame.getStatusCode());
}
closeSocket();
if (!mRepeatWrapper.isRepeatUpload()) {
closeExecutors();
}
}
return;
}
closeSocket();
if (!mErrorDispatched && !mForceCloseSocket) {
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onUploadError(SpeedTestError.SOCKET_ERROR, "mSocket error");
}
}
closeExecutors();
} catch (IOException | InterruptedException e) {
mReportInterval = false;
if (!mErrorDispatched) {
catchError(false, e.getMessage());
}
}
mErrorDispatched = false;
}
/**
* Write download request to server host.
*
* @param data HTTP request to send to initiate download process
*/
private void writeDownload(final byte[] data) {
connectAndExecuteTask(new Runnable() {
@Override
public void run() {
if (mSocket != null && !mSocket.isClosed()) {
try {
if ((mSocket.getOutputStream() != null) && (writeFlushSocket(data) != 0)) {
throw new SocketTimeoutException();
}
} catch (SocketTimeoutException e) {
SpeedTestUtils.dispatchSocketTimeout(mForceCloseSocket, mListenerList,
true, SpeedTestConst.SOCKET_WRITE_ERROR);
closeSocket();
closeExecutors();
} catch (IOException e) {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, true, e.getMessage());
closeExecutors();
}
}
}
}, true);
}
/**
* logout & disconnect FTP client.
*
* @param ftpclient ftp client
*/
private void disconnectFtp(final FTPClient ftpclient) {
try {
if (ftpclient.isConnected()) {
ftpclient.logout();
ftpclient.disconnect();
}
} catch (IOException ex) {
//ex.printStackTrace();
}
}
/**
* write and flush mSocket.
*
* @param data payload to write
* @return error status (-1 for error)
* @throws IOException mSocket io exception
*/
private int writeFlushSocket(final byte[] data) throws IOException {
final ExecutorService executor = Executors.newSingleThreadExecutor();
@SuppressWarnings("unchecked")
final Future future = executor.submit(new Callable() {
/**
* execute sequential write/flush task.
*
* @return status
*/
public Integer call() {
try {
mSocket.getOutputStream().write(data);
mSocket.getOutputStream().flush();
} catch (IOException e) {
return -1;
}
return 0;
}
});
int status;
try {
status = future.get(mSocketInterface.getSocketTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
status = -1;
} catch (InterruptedException | ExecutionException e) {
status = -1;
}
executor.shutdownNow();
return status;
}
/**
* catch an error.
*
* @param isDownload downloading task or uploading task
* @param errorMessage error message from Exception
*/
private void catchError(final boolean isDownload, final String errorMessage) {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, isDownload, errorMessage);
mTimeEnd = System.currentTimeMillis();
closeSocket();
closeExecutors();
}
/**
* get a download/upload report.
*
* @param mode speed test mode requested
* @return speed test report
*/
public SpeedTestReport getReport(final SpeedTestMode mode) {
BigDecimal temporaryPacketSize = BigDecimal.ZERO;
BigDecimal totalPacketSize = BigDecimal.ZERO;
switch (mode) {
case DOWNLOAD:
temporaryPacketSize = new BigDecimal(mDownloadTemporaryPacketSize);
totalPacketSize = mDownloadPckSize;
break;
case UPLOAD:
temporaryPacketSize = new BigDecimal(mUploadTempFileSize);
totalPacketSize = mUploadFileSize;
break;
default:
break;
}
long currentTime;
if (mTimeEnd == 0) {
currentTime = System.currentTimeMillis();
} else {
currentTime = mTimeEnd;
}
BigDecimal transferRateOps = BigDecimal.ZERO;
final int scale = mSocketInterface.getDefaultScale();
final RoundingMode roundingMode = mSocketInterface.getDefaultRoundingMode();
if (shallCalculateTransferRate(currentTime)) {
transferRateOps = temporaryPacketSize.divide(new BigDecimal(currentTime - mTimeStart)
.divide(SpeedTestConst.MILLIS_DIVIDER, scale, roundingMode), scale, roundingMode);
}
final BigDecimal transferRateBitps = transferRateOps.multiply(SpeedTestConst.BIT_MULTIPLIER);
BigDecimal percent = BigDecimal.ZERO;
SpeedTestReport report;
if (mRepeatWrapper.isRepeat()) {
report = mRepeatWrapper.getRepeatReport(scale, roundingMode, mode, currentTime, transferRateOps);
} else {
if (totalPacketSize != BigDecimal.ZERO) {
percent = temporaryPacketSize.multiply(SpeedTestConst.PERCENT_MAX).divide(totalPacketSize, scale,
roundingMode);
}
report = new SpeedTestReport(mode, percent.floatValue(),
mTimeStart, currentTime, temporaryPacketSize.longValueExact(), totalPacketSize.longValueExact(),
transferRateOps, transferRateBitps,
1);
}
return report;
}
/**
* Check setup time depending on elapsed time.
*
* @param currentTime elapsed time since upload/download has started
* @return status if transfer rate should be computed at this time
*/
private boolean shallCalculateTransferRate(final long currentTime) {
final long elapsedTime = currentTime - mTimeStart;
switch (mSpeedTestMode) {
case DOWNLOAD:
return (elapsedTime > mSocketInterface.getDownloadSetupTime());
case UPLOAD:
return (elapsedTime > mSocketInterface.getUploadSetupTime());
default:
}
return true;
}
/**
* Get FTP file size.
*
* @param ftpClient ftp client
* @param filePath remote file path
* @return file size
* @throws Exception file read/write IOException
*/
private long getFileSize(final FTPClient ftpClient, final String filePath) throws IOException {
long fileSize = 0;
final FTPFile[] files = ftpClient.listFiles(filePath);
if (files.length == 1 && files[0].isFile()) {
fileSize = files[0].getSize();
}
return fileSize;
}
/**
* start FTP download with specific port, user, password.
*
* @param hostname ftp host
* @param uri ftp uri
* @param user ftp username
* @param password ftp password
*/
public void startFtpDownload(final String hostname,
final int port,
final String uri,
final String user,
final String password) {
mSpeedTestMode = SpeedTestMode.DOWNLOAD;
mErrorDispatched = false;
mForceCloseSocket = false;
if (mReadExecutorService == null || mReadExecutorService.isShutdown()) {
mReadExecutorService = Executors.newSingleThreadExecutor();
}
mReadExecutorService.execute(new Runnable() {
@Override
public void run() {
final FTPClient ftpclient = new FTPClient();
try {
ftpclient.connect(hostname, port);
ftpclient.login(user, password);
ftpclient.enterLocalPassiveMode();
ftpclient.setFileType(FTP.BINARY_FILE_TYPE);
mDownloadTemporaryPacketSize = 0;
mTimeStart = System.currentTimeMillis();
mTimeEnd = 0;
if (mRepeatWrapper.isFirstDownload()) {
mRepeatWrapper.setFirstDownloadRepeat(false);
mRepeatWrapper.setStartDate(mTimeStart);
}
mDownloadPckSize = new BigDecimal(getFileSize(ftpclient, uri));
if (mRepeatWrapper.isRepeatDownload()) {
mRepeatWrapper.updatePacketSize(mDownloadPckSize);
}
mFtpInputstream = ftpclient.retrieveFileStream(uri);
if (mFtpInputstream != null) {
final byte[] bytesArray = new byte[SpeedTestConst.READ_BUFFER_SIZE];
int read;
while ((read = mFtpInputstream.read(bytesArray)) != -1) {
mDownloadTemporaryPacketSize += read;
if (mRepeatWrapper.isRepeatDownload()) {
mRepeatWrapper.updateTempPacketSize(read);
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveDownloadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onDownloadProgress(report.getProgressPercent(), report);
}
}
if (mDownloadTemporaryPacketSize == mDownloadPckSize.longValueExact()) {
break;
}
}
mFtpInputstream.close();
mTimeEnd = System.currentTimeMillis();
mReportInterval = false;
final SpeedTestReport report = mSocketInterface.getLiveDownloadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onDownloadFinished(report);
}
} else {
mReportInterval = false;
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, true, "cant create stream " +
"from uri " + uri + " with reply code : " + ftpclient.getReplyCode());
}
if (!mRepeatWrapper.isRepeatDownload()) {
closeExecutors();
}
} catch (IOException e) {
//e.printStackTrace();
mReportInterval = false;
catchError(true, e.getMessage());
} finally {
mErrorDispatched = false;
mSpeedTestMode = SpeedTestMode.NONE;
disconnectFtp(ftpclient);
}
}
});
}
/**
* Start FTP upload.
*
* @param hostname ftp host
* @param port ftp port
* @param uri upload uri
* @param fileSizeOctet file size in octet
* @param user username
* @param password password
*/
public void startFtpUpload(final String hostname,
final int port,
final String uri,
final int fileSizeOctet,
final String user,
final String password) {
mSpeedTestMode = SpeedTestMode.UPLOAD;
mUploadFileSize = new BigDecimal(fileSizeOctet);
mForceCloseSocket = false;
mErrorDispatched = false;
if (mWriteExecutorService == null || mWriteExecutorService.isShutdown()) {
mWriteExecutorService = Executors.newSingleThreadExecutor();
}
mWriteExecutorService.execute(new Runnable() {
@Override
public void run() {
final FTPClient ftpClient = new FTPClient();
final RandomGen randomGen = new RandomGen();
RandomAccessFile uploadFile = null;
try {
ftpClient.connect(hostname, port);
ftpClient.login(user, password);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
byte[] fileContent = new byte[]{};
if (mSocketInterface.getUploadStorageType() == UploadStorageType.RAM_STORAGE) {
/* generate a file with size of fileSizeOctet octet */
fileContent = randomGen.generateRandomArray(fileSizeOctet);
} else {
uploadFile = randomGen.generateRandomFile(fileSizeOctet);
uploadFile.seek(0);
}
mFtpOutputstream = ftpClient.storeFileStream(uri);
if (mFtpOutputstream != null) {
mUploadTempFileSize = 0;
final int uploadChunkSize = mSocketInterface.getUploadChunkSize();
final int step = fileSizeOctet / uploadChunkSize;
final int remain = fileSizeOctet % uploadChunkSize;
mTimeStart = System.currentTimeMillis();
mTimeEnd = 0;
if (mRepeatWrapper.isFirstUpload()) {
mRepeatWrapper.setFirstUploadRepeat(false);
mRepeatWrapper.setStartDate(mTimeStart);
}
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updatePacketSize(mUploadFileSize);
}
if (mForceCloseSocket) {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, "");
} else {
for (int i = 0; i < step; i++) {
final byte[] chunk = SpeedTestUtils.readUploadData(mSocketInterface
.getUploadStorageType(),
fileContent,
uploadFile,
mUploadTempFileSize,
uploadChunkSize);
mFtpOutputstream.write(chunk, 0, uploadChunkSize);
mUploadTempFileSize += uploadChunkSize;
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updateTempPacketSize(uploadChunkSize);
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int j = 0; j < mListenerList.size(); j++) {
mListenerList.get(j).onUploadProgress(report.getProgressPercent(), report);
}
}
}
if (remain != 0) {
final byte[] chunk = SpeedTestUtils.readUploadData(mSocketInterface
.getUploadStorageType(),
fileContent,
uploadFile,
mUploadTempFileSize,
remain);
mFtpOutputstream.write(chunk, 0, remain);
mUploadTempFileSize += remain;
if (mRepeatWrapper.isRepeatUpload()) {
mRepeatWrapper.updateTempPacketSize(remain);
}
}
if (!mReportInterval) {
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int j = 0; j < mListenerList.size(); j++) {
mListenerList.get(j).onUploadProgress(SpeedTestConst.PERCENT_MAX.floatValue(),
report);
}
}
mTimeEnd = System.currentTimeMillis();
}
mFtpOutputstream.close();
mReportInterval = false;
final SpeedTestReport report = mSocketInterface.getLiveUploadReport();
for (int i = 0; i < mListenerList.size(); i++) {
mListenerList.get(i).onUploadFinished(report);
}
if (!mRepeatWrapper.isRepeatUpload()) {
closeExecutors();
}
} else {
mReportInterval = false;
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, "cant create stream " +
"from uri " + uri + " with reply code : " + ftpClient.getReplyCode());
}
} catch (SocketTimeoutException e) {
//e.printStackTrace();
mReportInterval = false;
mErrorDispatched = true;
if (!mForceCloseSocket) {
SpeedTestUtils.dispatchSocketTimeout(mForceCloseSocket, mListenerList,
false, SpeedTestConst.SOCKET_WRITE_ERROR);
} else {
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, e.getMessage());
}
closeSocket();
closeExecutors();
} catch (IOException e) {
//e.printStackTrace();
mReportInterval = false;
mErrorDispatched = true;
SpeedTestUtils.dispatchError(mForceCloseSocket, mListenerList, false, e.getMessage());
closeExecutors();
} finally {
mErrorDispatched = false;
mSpeedTestMode = SpeedTestMode.NONE;
disconnectFtp(ftpClient);
if (uploadFile != null) {
try {
uploadFile.close();
randomGen.deleteFile();
} catch (IOException e) {
//e.printStackTrace();
}
}
}
}
});
}
/**
* Close socket streams and mSocket object.
*/
public void closeSocket() {
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
}
}
}
/**
* close socket / stop download/upload operations.
*/
public void forceStopTask() {
mSpeedTestMode = SpeedTestMode.NONE;
mForceCloseSocket = true;
if (mFtpInputstream != null) {
try {
mFtpInputstream.close();
} catch (IOException e) {
//e.printStackTrace();
}
}
if (mFtpOutputstream != null) {
try {
mFtpOutputstream.close();
} catch (IOException e) {
//e.printStackTrace();
}
}
}
/**
* Shutdown threadpool and wait for task completion.
*/
public void shutdownAndWait() {
closeExecutors();
try {
mReadExecutorService.awaitTermination(SpeedTestConst.THREADPOOL_WAIT_COMPLETION_MS, TimeUnit.MILLISECONDS);
mWriteExecutorService.awaitTermination(SpeedTestConst.THREADPOOL_WAIT_COMPLETION_MS, TimeUnit.MILLISECONDS);
mReportExecutorService.awaitTermination(SpeedTestConst.THREADPOOL_WAIT_COMPLETION_MS,
TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
//e.printStackTrace();
}
}
/**
* reset report threadpool if necessary.
*/
public void renewReportThreadPool() {
if (mReportExecutorService == null || mReportExecutorService.isShutdown()) {
mReportExecutorService = Executors.newScheduledThreadPool(SpeedTestConst.THREAD_POOL_REPORT_SIZE);
}
}
/**
* retrieve threadpool used to publish reports.
*
* @return report threadpool
*/
public ScheduledExecutorService getReportThreadPool() {
return mReportExecutorService;
}
/**
* retrieve current speed test mode.
*
* @return speed test mode (UPLOAD/DOWNLOAD/NONE)
*/
public SpeedTestMode getSpeedTestMode() {
return mSpeedTestMode;
}
}