Java tutorial
/* * The MIT License (MIT) * <p/> * Copyright (c) 2016 Bertrand Martel * <p/> * 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: * <p/> * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * <p/> * 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<ISpeedTestListener> 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; /** * milliseconds divider for calculating the threshold before updating the calculation of the upload speed */ private static final long MILLISECONDS_DIVIDER = 1000; /** * Build socket. * * @param socketInterface interface shared between repeat wrapper and speed test socket */ public SpeedTestTask(final ISpeedTestSocket socketInterface, final List<ISpeedTestListener> 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<Integer> 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, mode)) { 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; } private boolean shallCalculateTransferRate(final long currentTime, final SpeedTestMode mode) { final long elapsedTime = currentTime - mTimeStart; boolean response; switch (mode) { case DOWNLOAD: response = elapsedTime != 0; break; case UPLOAD: default: response = elapsedTime > MILLISECONDS_DIVIDER; break; } return response; } /** * 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; } }