-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pivotal ID #186675047: FTP connection pool (#792)
https://www.pivotaltracker.com/story/show/186675047 - added pooled FTP connection
- Loading branch information
Showing
12 changed files
with
209 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
coverage=0.31 | ||
coverage=0.30 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
client/ftp-webclient/src/main/kotlin/ebi/ac/uk/ftp/FTPClientPool.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package ebi.ac.uk.ftp | ||
|
||
import mu.KotlinLogging | ||
import org.apache.commons.net.ftp.FTPClient | ||
import org.apache.commons.pool2.BasePooledObjectFactory | ||
import org.apache.commons.pool2.PooledObject | ||
import org.apache.commons.pool2.impl.DefaultPooledObject | ||
import org.apache.commons.pool2.impl.GenericObjectPool | ||
import kotlin.time.Duration.Companion.milliseconds | ||
|
||
private val logger = KotlinLogging.logger {} | ||
|
||
/** | ||
* Pooled FTP client. Allows to re use FTPClient instances so socket connections and ftp logging does not need to be | ||
* performed on each FTP operation. | ||
*/ | ||
internal class FTPClientPool( | ||
private val ftpUser: String, | ||
private val ftpPassword: String, | ||
private val ftpUrl: String, | ||
private val ftpPort: Int, | ||
private val ftpClientPool: GenericObjectPool<FTPClient> = createFtpPool(ftpUser, ftpPassword, ftpUrl, ftpPort), | ||
) { | ||
fun <T> execute(action: (FTPClient) -> T): T { | ||
val ftpClient = ftpClientPool.borrowObject() | ||
return try { | ||
action(ftpClient) | ||
} finally { | ||
ftpClientPool.returnObject(ftpClient) | ||
} | ||
} | ||
|
||
private class FTPClientFactory( | ||
private val ftpUser: String, | ||
private val ftpPassword: String, | ||
private val ftpUrl: String, | ||
private val ftpPort: Int, | ||
) : BasePooledObjectFactory<FTPClient>() { | ||
override fun create(): FTPClient { | ||
val ftp = ftpClient(3000.milliseconds, 3000.milliseconds) | ||
logger.info { "Connecting to $ftpUrl, $ftpPort" } | ||
ftp.connect(ftpUrl, ftpPort) | ||
ftp.login(ftpUser, ftpPassword) | ||
ftp.enterLocalPassiveMode() | ||
return ftp | ||
} | ||
|
||
override fun wrap(ftpClient: FTPClient): PooledObject<FTPClient> { | ||
return DefaultPooledObject(ftpClient) | ||
} | ||
|
||
override fun destroyObject(p: PooledObject<FTPClient>) { | ||
val ftpClient = p.`object` | ||
if (ftpClient.isConnected) { | ||
ftpClient.logout() | ||
ftpClient.disconnect() | ||
} | ||
} | ||
|
||
@Suppress("TooGenericExceptionCaught") | ||
override fun validateObject(p: PooledObject<FTPClient>): Boolean { | ||
val ftpClient = p.`object` | ||
return try { | ||
ftpClient.sendNoOp() | ||
} catch (e: Exception) { | ||
logger.error(e) { "Error checking ftp connection" } | ||
false | ||
} | ||
} | ||
} | ||
|
||
private companion object { | ||
private const val MIN_CONNECTION = 2 | ||
|
||
fun createFtpPool( | ||
ftpUser: String, | ||
ftpPassword: String, | ||
ftpUrl: String, | ||
ftpPort: Int, | ||
): GenericObjectPool<FTPClient> { | ||
val factory = FTPClientFactory(ftpUser, ftpPassword, ftpUrl, ftpPort) | ||
var connections = GenericObjectPool(factory) | ||
connections.minIdle = MIN_CONNECTION | ||
return connections | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
client/ftp-webclient/src/test/kotlin/ebi/ac/uk/ftp/FtpClientTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package ebi.ac.uk.ftp | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import java.io.File | ||
import java.nio.file.Paths | ||
import kotlin.io.path.createTempFile | ||
import kotlin.io.path.inputStream | ||
import kotlin.io.path.outputStream | ||
import kotlin.io.path.writeText | ||
|
||
class FtpClientTest { | ||
private val ftpServer = createFtpServer().apply { start() } | ||
private val testInstance = FtpClient.create(FTP_USER, FTP_PASSWORD, ftpServer.getUrl(), ftpServer.ftpPort) | ||
|
||
@Test | ||
fun `upload a file, list it and download it`() { | ||
val tempFile = createTempFile() | ||
tempFile.writeText("test-file") | ||
tempFile.writeText("test-file") | ||
|
||
val rootPath = Paths.get("") | ||
val filePath = rootPath.resolve("test-file.txt") | ||
|
||
testInstance.uploadFile(filePath, { tempFile.inputStream() }) | ||
|
||
val files = testInstance.listFiles(rootPath) | ||
assertThat(files).hasSize(1) | ||
val file = files.first() | ||
assertThat(file.name).isEqualTo("test-file.txt") | ||
|
||
val outputFile = createTempFile() | ||
outputFile.outputStream().use { testInstance.downloadFile(filePath, it) } | ||
assertThat(outputFile).hasSameContentAs(tempFile) | ||
} | ||
|
||
companion object { | ||
fun createFtpServer(): FtpServer { | ||
return FtpServer.createServer( | ||
FtpConfig( | ||
sslConfig = SslConfig(File(this::class.java.getResource("/mykeystore.jks").toURI()), "123456"), | ||
userName = FTP_USER, | ||
password = FTP_PASSWORD | ||
) | ||
) | ||
} | ||
|
||
const val FTP_USER = "ftpUser" | ||
const val FTP_PASSWORD = "ftpPassword" | ||
} | ||
} |
50 changes: 0 additions & 50 deletions
50
client/ftp-webclient/src/test/kotlin/ebi/ac/uk/ftp/SimpleFtpClientTest.kt
This file was deleted.
Oops, something went wrong.
Binary file not shown.
Oops, something went wrong.