高度な構成
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.com/2009/02/vsftpd-210-released.html (英語) を参照してください。
現在、Apache FTPSClient はこの機能をサポートしていません。NET-408 [Apache] (英語) を参照してください。
以下の解決策(Stack Overflow (英語) 提供)は、sun.security.ssl.SSLSessionContextImpl のリフレクションを使用しているため、他の JVM では動作しない可能性があります。この Stack Overflow の回答は 2015 年に投稿され、この解決策は Spring Integration チームによって JDK 1.8.0_112 でテストされています。
次の例は、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());
}
}
}
}