高度な構成
DefaultFtpSessionFactory
は、(Spring Integration 2.0 以降)Apache Commons ネット (英語) である基礎となるクライアント API を抽象化します。これにより、org.apache.commons.net.ftp.FTPClient
の低レベルの構成の詳細から解放されます。いくつかの一般的なプロパティがセッションファクトリで公開されています(バージョン 4.0 以降、これには connectTimeout
、defaultTimeout
、dataTimeout
が含まれるようになりました)。ただし、より高度な構成(アクティブモードのポート範囲の設定など)を実現するために、低レベルの FTPClient
構成にアクセスする必要がある場合があります。そのために、AbstractFtpSessionFactory
(すべての FTP セッションファクトリの基本クラス)は、次のリストに示す 2 つの後処理メソッドの形式でフックを公開します。
/**
* Will handle additional initialization after client.connect() method was invoked,
* but before any action on the client has been taken
*/
protected void postProcessClientAfterConnect(T t) throws IOException {
// NOOP
}
/**
* Will handle additional initialization before client.connect() method was invoked.
*/
protected void postProcessClientBeforeConnect(T client) throws IOException {
// NOOP
}
ご覧のとおり、これら 2 つのメソッドにはデフォルトの実装はありません。ただし、次の例に示すように、DefaultFtpSessionFactory
を継承することにより、これらのメソッドをオーバーライドして、FTPClient
のより高度な構成を提供できます。
public class AdvancedFtpSessionFactory extends DefaultFtpSessionFactory {
protected void postProcessClientBeforeConnect(FTPClient ftpClient) throws IOException {
ftpClient.setActivePortRange(4000, 5000);
}
}
FTPS および共有 SSLSession
FTP over SSL または TLS を使用する場合、一部のサーバーでは、制御接続とデータ接続で同じ SSLSession
を使用する必要があります。これは、データ接続の「盗用」を防ぐためです。詳細については、scarybeastsecurity.blogspot.cz/2009/02/vsftpd-210-released.html (英語) を参照してください。
現在、Apache FTPSClient はこの機能をサポートしていません。NET-408 [Apache] (英語) を参照してください。
次のソリューションは、Stack Overflow (英語) の厚意により、sun.security.ssl.SSLSessionContextImpl
でリフレクションを使用するため、他の JVM では機能しない可能性があります。スタックオーバーフローの回答は 2015 年に提出され、ソリューションは JDK 1.8.0_112 で Spring Integration チームによってテストされました。
次の例は、FTPS セッションを作成する方法を示しています。
@Bean
public DefaultFtpsSessionFactory sf() {
DefaultFtpsSessionFactory sf = new DefaultFtpsSessionFactory() {
@Override
protected FTPSClient createClientInstance() {
return new SharedSSLFTPSClient();
}
};
sf.setHost("...");
sf.setPort(21);
sf.setUsername("...");
sf.setPassword("...");
sf.setNeedClientAuth(true);
return sf;
}
private static final class SharedSSLFTPSClient extends FTPSClient {
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
context.setSessionCacheSize(0); // you might want to limit the cache
try {
final Field sessionHostPortCache = context.getClass()
.getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method = cache.getClass().getDeclaredMethod("put", Object.class,
Object.class);
method.setAccessible(true);
String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, session);
key = String.format("%s:%s", socket.getInetAddress().getHostAddress(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, session);
}
catch (NoSuchFieldException e) {
// Not running in expected JRE
logger.warn("No field sessionHostPortCache in SSLSessionContext", e);
}
catch (Exception e) {
// Not running in expected JRE
logger.warn(e.getMessage());
}
}
}
}