Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 12111

Why does doWork in CoroutineWorker hang when exception is thrown on bad login to FTP server?

$
0
0

I am trying to make a connection with an FTP server (vsftpd with SSL/TLS configured including implicit passive mode) and list all files in the "." directory. I use Android Studio with Kotlin.

Whenever I invoke the enqueueTask(workManager) and wait 10 seconds (initial delay of the worker), the FTP client connects to the vsftpd server succesfully but upon giving wrong user and pass credentials (which leads to the code throwing ConnectException due to loggedIn being false) the exception is not re-thrown immediately and only thrown after some minutes of waiting.

Why is this behaviour occuring and how to resolve it such that the exception is thrown as soon as it occurs so that it can be caught by the withContext(Dispatchers.IO) for retry?

build.gradle:

buildscript {    ext.kotlin_version = "1.3.72"    repositories {        google()        jcenter()    }    dependencies {        classpath "com.android.tools.build:gradle:4.0.1"        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"    }}allprojects {    repositories {        google()        jcenter()    }}apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'android {    compileSdkVersion 28    buildToolsVersion '29.0.3'    kotlinOptions {        jvmTarget = "1.8"    }    defaultConfig {        applicationId "com.app.backupapp"        minSdkVersion 27        targetSdkVersion 28        versionCode 1        versionName "1.0"        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'        }    }}dependencies {    implementation fileTree(dir: "libs", include: ["*.jar"])    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"    implementation 'androidx.core:core-ktx:1.3.1'    implementation 'androidx.appcompat:appcompat:1.2.0'    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'    implementation 'commons-net:commons-net:3.6' // for ftp    implementation 'androidx.work:work-runtime-ktx:2.5.0' // for workmanager    testImplementation 'junit:junit:4.12'    androidTestImplementation 'androidx.test.ext:junit:1.1.1'    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'}

PeriodicWorker class:

class PeriodicWorker(    appContext: Context,    private val workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {    companion object {        private const val TAG: String = "PeriodicWorker"        private const val TIME: Long = 3        private val UNIT_TIME: TimeUnit = TimeUnit.HOURS        fun enqueueTask(workManager: WorkManager) {            val periodicWork = PeriodicWorkRequestBuilder<PeriodicWorker>(                TIME, UNIT_TIME            )                .setInitialDelay(10, TimeUnit.SECONDS) // for testing purposes                .setBackoffCriteria(                    BackoffPolicy.LINEAR,                    PeriodicWorkRequest.MIN_BACKOFF_MILLIS,                    TimeUnit.SECONDS                )                .build()            workManager.enqueueUniquePeriodicWork("unique", ExistingPeriodicWorkPolicy.KEEP, periodicWork)        }    }    private val ftpHandler: FtpHandler = FtpHandler()    override suspend fun doWork(): Result {        return withContext(Dispatchers.IO) {            try {                // Note: throw ConnectException --> when throw exception here, the exception is caught immediately by catch block                val filesInFtpServer: Array<String>? = ftpHandler.listFileNamesFtpServer() // Note 2: Should throw exception but doesn't immediately do that                Result.success()            } catch (e: Exception) {                Log.e(TAG, "doWork: ${e.message}, ${e.stackTrace.joinToString { ", " }}")                Result.retry()            }        }    }}

ftpHandler class:

class FtpHandler {    companion object {        private const val DATA_TIMEOUT_MS = 1000 * 5         private const val CONNECTION_TIMEOUT_MS = 1000 * 5        private const val TAG = "FtpHandler"    }    private val client: FTPSClient = FTPSClient("TLS", true)    private val ftpHost = "10.0.2.2"    private val ftpPort = 21    private val ftpUsername = "wrong_user"    private val ftpPassword = "wrong_pass"    init {        client.setDataTimeout(DATA_TIMEOUT_MS)        client.connectTimeout = CONNECTION_TIMEOUT_MS    }    private fun login(): Boolean {        // Establish a connection with the FTP Server        client.connect(ftpHost, ftpPort)        client.execPBSZ(0)        client.execPROT("P")         client.enterLocalPassiveMode()        // Login with given username and pass        return client.login(ftpUsername, ftpPassword)    }    fun listFileNamesFtpServer(directory: String = "."): Array<String>? {        var res: Array<String>? = null        try {            val loggedIn: Boolean = login()            if (loggedIn) {                if (FTPReply.isPositiveCompletion(client.replyCode)) {                    client.changeWorkingDirectory(directory)                    res = client.listNames()                } else {                    throw ConnectException()                }            } else {                throw ConnectException()            }        } catch (e: Exception) {            throw e        } finally {            try {                client.logout()                client.disconnect()            } catch (e: IOException) {                throw e            }        }        return res    }}

I suspect it has to do with the timeouts of the FTP socket that might be interleaved at some points of the execution of login function. I tried using another test function that does dummy http request and waits 3000 milliseconds until it throws an Exception and even that immediately triggered exception and caught in the context Dispatchers.IO.


Viewing all articles
Browse latest Browse all 12111

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>