博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用commons-pool管理FTP连接
阅读量:5986 次
发布时间:2019-06-20

本文共 5527 字,大约阅读时间需要 18 分钟。

使用commons-pool管理FTP连接

背景

在文章,已经完成一版对FTP连接的管理,设计了模板方法,为工具类上传和下载文件方法的提供获取对象和释放对象支持。

此番重新造轮子更多地考虑功能复用的角度,支持更多可配置参数,不止是连接池相关的属性;只考虑维护同一个连接请求多个连接对象的情况,将多个不同请求的情况交给外部管理,由外部定制,类似多数据源数据库连接的方式;重新审视模板方法的使用,在不引入模板的方法,设计封装对象池管理功能,以更自然的方式获取对象和释放对象。

思路

整体的思路来自BasicDataSource,它是javax.sql.DataSource的具体实现,实现的是数据库连接池,使用上完全感觉不到对象池的存在,通过dataSource获取对象connection,释放对象则使用connection.close()即可。然而,与javax.sql.DataSourcejava.sql.Connection不同的是,JDK中并没有支持FTP协议的类似的框架;另一个问题则是,项目中已经使用commons-net来建立FTP连接,使用FTPClient等API了,如何将具体实现整合到要新定义的接口中,似乎是本末倒置的。

实现

整体框架

首先定义整体框架,类似DataSource

public interface FTPManager extends AutoCloseable {    FTPConnection getFTPConnection() throws FTPException ;}

定义连接对象,

public interface FTPConnection extends Wrapper, AutoCloseable {    void close() throws FTPException;        boolean isClosed() throws FTPException;        //ftp|ftps|ftp:http -- subprotocol    //String getSchema() throws FTPException;}

从这个框架出发,获取连接对象使用ftpManager.getFTPConnection,释放对象使用ftpConnection.close

整理配置属性

引入主角FTPCPManager,在FTPCPManager定义和连接相关的属性,抽取一个父类PoolProperties专门用于配置对象池相关的配置。

public class FTPCPManager extends PoolProperties implements FTPManager {    protected String url;        protected String username;    protected String password;    protected String proxyHost = null;    protected int proxyPort = 80;    protected String proxyUser = null;    protected String proxyPassword = null;    protected String encoding = StandardCharsets.UTF_8.name();    protected long keepAliveTimeout = -1;    protected int controlKeepAliveReplyTimeout = -1;    protected String serverTimeZoneId = null;    protected int bufferSize = -1;    protected int connectTimeout = -1;    protected String localActive = "false";}

类似地,若使用Spring的xml配置,配置FTPCPManager或许是这样的,

关于对象池的属性的说明请参考更多网络文章,或者官方文档。

获取对象

这是FTPCPManager最核心的部分了,入口是getFTPConnection方法,

public FTPConnection getFTPConnection() throws FTPException {    return createFTPManager().getFTPConnection();}
protected synchronized FTPManager createFTPManager() {    if(ftpManager != null) {        return ftpManager;    }    //create connection factory    IFTPClientFactory ftpClientFactory = createFTPClientFactory();    PoolingFTPManager newManager = new PoolingFTPManager(ftpClientFactory, this);    connectionPool = newManager.getPool();    this.ftpManager = newManager;    return newManager;}

FTPCPManager做了一个特殊处理,在内部维护了新的FTPManager类型变量,不同的是它带有对象池管理的功能,它存在的意义就是将对象池和对象工厂组合起来,这样的处理方式减轻了FTPCPManager的负担,职责更少,只提供重要接口,重要的实现还是交给被代理的成员。(当然,这里也可以有不同的看法)。createFTPClientFactory会根据url属性的协议分别创建不同的对象工厂,如FTPClientFactoryFTPSClientFactory等。

PoolingFTPManager的构造方法,需要对象工厂及连接池配置属性两个参数,FTPCPManager正好继承扩展了PoolProperties类,作为连接池配置参数很合适。所以构造被代理的成员,即newManager = new PoolingFTPManager(ftpClientFactory, this)

构造好PoolingFTPManager的实例后,就可以获取FTPConnection连接对象了,接下来就是对象池的功能了。整体时序图如下,

图片描述
对象的获取最终还是对象池与对象工厂的事情。

释放对象

为了让FTPConnection执行close方法的时候能够释放自己,将自己return到对象池,必须对FTPConnection做一些封装,连接对象需要记住最初的对象池对象,而对象池需要通过对象工厂来构造,通过这些条件代码的实现思路如下,

在构造PoolingFTPManager的同时也针对FTP对象工厂进行了封装,把原来的IFTPClientFactory封装成PoolableConnectionFactory类型,并且PoolableConnectionFactory持有GenericObjectPool类型的的对象池变量。在构造完GenericObjectPool对象池后,将对象池引用设置到PoolableConnectionFactory中。

PoolingFTPManager(IFTPClientFactory clientFactory, PoolProperties poolProperties) {    //create object factory    _connectionFactory = new PoolableConnectionFactory(clientFactory);    GenericObjectPoolConfig config = new GenericObjectPoolConfig();    // set config        _pool = new GenericObjectPool
(_connectionFactory, config); _connectionFactory.setPool(_pool);//反向引用}

此外,在执行PoolableConnectionFactorymakeObject方法,对生成的对象做一次封装,传递PoolableConnectionFactory持有的对象池给新生成的的对象。

public PooledObject
makeObject() throws Exception { FTPClient ftpClient = factory.getFTPClient(); FTPClientWrapperConnection wrapperConnection = new FTPClientWrapperConnection(ftpClient,pool); return new DefaultPooledObject
(wrapperConnection);}

这个FTPClientWrapperConnection类就是关键了。FTPConnection执行close方法能将自己释放,return到对象池,就是由FTPClientWrapperConnection具体实现的。

public void close() throws FTPException {    try {        if(pool != null && !pool.isClosed()) {            pool.returnObject(this);        } else {            if(ftpClient!=null) {                ftpClient.logout();                ftpClient.disconnect();            }        }    } catch (Exception e) {        //swallow everything    } finally {        _closed = true;    }}

简单测试

用一个测试来表现这个获取和释放对象的功能,

public class FTPCPManagerTest {    @Test    public void test1() throws Exception {        FTPCPManager manager = new FTPCPManager();        manager.setUrl("ftp://127.0.0.1");        manager.setUsername("sa");        manager.setPassword("sa");        manager.setInitialSize(2);        manager.setKeepAliveTimeout(1 * 60);                FTPConnection conn = manager.getFTPConnection();        assertTrue(manager.getNumActive() == 1);        assertTrue(manager.getNumIdle() == 1);        conn.close();        assertTrue(manager.getNumActive() == 0);        assertTrue(manager.getNumIdle() == 2);        manager.close();    }}

首先initialSize设置了对象池初始大小,在构造对象池的时候就调用了两次对象工厂的makeObject方法生成两个对象。然后是通过manager获取一次对象,此时检测对象池的被借出的对象manager.getNumActive() == 1是否成立,检测对象池保留的对象manager.getNumIdle() == 1是否成立。接下里是调用连接对象的close方法,再次检测比较对象池保留的对象是否manager.getNumIdle() == 2。如果以上断言都成立,证明对象的获取和释放使用到了对象池管理而且能够正常运行。

总结

至此,使用commons-pool管理FTP连接的功能算基本完成了。与文章中的FTP工具相比还缺少上传下载等功能的封装,而这些功能将会交给另外的工程来完成。

项目地址:

转载地址:http://txylx.baihongyu.com/

你可能感兴趣的文章
如何在MySQL中设置外键约束
查看>>
【转载】关于什么是测试专家的讨论
查看>>
jQuery 定时局部刷新(setInterval)方法总结
查看>>
享元模式(Flyweight Pattern)
查看>>
windows pip安装 更新
查看>>
记录 无限极分类 做文章分类的 后台显示
查看>>
Android View的滑动 动画
查看>>
结巴-关键词提取
查看>>
手工启动oracle EM
查看>>
使用xml配置文件通过post方法调用Jenkins api创建job
查看>>
System.BadImageFormatException
查看>>
Codeforces 838 B - Diverging Directions
查看>>
CSS实例:图片导航块
查看>>
如何让PL/SQL Developer记住密码?
查看>>
数据库事务管理
查看>>
jq插件
查看>>
PHP判断变量是否为整型
查看>>
centos7.6安装teamviewer-原来还可以用yum 安装rpm包,自动解决依赖,太爽了
查看>>
【资源下载】分享个嵌入式开发的入门教程(包含视频)
查看>>
《.NET 进阶指南》读书笔记1------NET程序集与普通EXE文件的区别
查看>>