アプリ開発ときどきアウトドア

主にJavaを使ったアプリ開発やトラブルシューティング等のノウハウ、キャンプや登山の紹介や体験談など。

1. システムエンジニアリング Java

Apache FTPClientでのコマンドのトレース

投稿日:


Apache FTPClientを使って、JavaアプリケーションからFTP操作が可能となります。
この際、FTPクライアントとFTPサーバ間との通信内容をログに出力する方法を説明します。

概要

  • Apache FTPClientは、Apache Commons Netライブラリの一部です。
    commons-net(3.6版)のjavadocはこちらをご覧ください。
  • 動作確認や単体試験時では、開発環境のIDE(デバッグ機能)を使って、アプリとFTPサーバとの通信内容を検証できます。結合試験や本番環境等では、IDE等のツールは一般的に導入されていることは少なく、環境固有の問題が発生した場合は、問題の検証が難しくなります。
    このような事態に備えて、アプリとFTPサーバとの通信をトレースできるログを出力しておきます。こうすることで、動作確認や単体試験時のエビデンスとしても使用できます。
  • 私のこれまでの経験上、このレベルの設計や実装方針は細かすぎて開発者任せになることが多い。アプリ開発者からすると、製造や単体試験をやりきることに主眼が置かれ、今回のような後工程での事象に対して考慮するような考えは乏しいです。設計、開発ガイド、教育等で「後工程でのトラブル解決が容易になるような考慮」を盛り込むことをおすすめします。
  • 通常、このようなログは細かすぎて環境が変わると大量のログが出力される可能性があります。そのため、必要な場合だけログ出力するようTRACEレベルでログ出力することをお薦めします。(感覚的には、DEBUGは点で動作を把握、TRACEは面で動作を把握するイメージで使い分けています。)

サンプルプログラム

サンプルの説明

主にFTPClientクラスを使って、FTP操作を行います。
このFTPClientに対して、addProtocolCommandListenerメソッドでトレースするためのクラス(ProtocolCommandListener)を指定します。
ProtocolCommandListenerでは、FTPClientでコマンドを実行する際に呼び出されるprotocolCommandSentメソッド、FTPサーバからの応答があった場合に呼び出されるprotocolReplyReceivedメソッドが定義されています。目的に応じて、それぞれのメソッドでログを出力します。
なお、FTP操作部分のコードですが、サンプルのため簡易的な実装になっています。業務で使用する場合、FTPReply#isPositiveCompletion()等を使って、コマンド個別の正常性確認をお薦めします。

package test;

import java.io.IOException;

import org.apache.commons.net.ProtocolCommandEvent;
import org.apache.commons.net.ProtocolCommandListener;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FTPCommandTraceTest {

	private static final String FTP_SERVER = "192.168.220.3";
	private static final String FTP_USER_NAME = "valuser";
	private static final String FTP_USER_PASS = "valuser";

	private static Logger logger = LoggerFactory.getLogger(FTPCommandTraceTest.class);

	public static void main(String[] args) throws IOException {

		FTPClient client = new FTPClient();
		client.addProtocolCommandListener(new ExampleCommandListener());

		// FTPサーバ操作例
		client.connect(FTP_SERVER);
		client.login(FTP_USER_NAME, FTP_USER_PASS);
		for (FTPFile file : client.listFiles(".")) {
			logger.debug("name={}", file.getName());
		}
		client.quit();
		client.disconnect();
	}

}

class ExampleCommandListener implements ProtocolCommandListener {

	private static Logger logger = LoggerFactory.getLogger(ExampleCommandListener.class);

	public void protocolCommandSent(ProtocolCommandEvent event) {
		logger.trace("> " + event.getCommand());
	}

	public void protocolReplyReceived(ProtocolCommandEvent event) {
		StringBuilder sb = new StringBuilder();
		logger.trace("< " + event.getMessage());
	}

}

実行に必要なライブラリはcommons-netとなります。
ログ出力のために、slf4j、slf4j-simpleを追加しています。

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>test</groupId>
	<artifactId>ftp-example</artifactId>
	<version>1.0.0</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<dependencies>

		<!-- FTP -->
		<dependency>
			<groupId>commons-net</groupId>
			<artifactId>commons-net</artifactId>
			<version>3.6</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
			<version>1.7.25</version>
		</dependency>

	</dependencies>

</project>

参考ですが、今回の例ではログ出力ライブラリとしてslf4j-simpleを使用しています。このライブラリで、trace以上のログを出力するための設定を行っています。

# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.

# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
#org.slf4j.simpleLogger.defaultLogLevel=info
org.slf4j.simpleLogger.defaultLogLevel=trace

# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
#org.slf4j.simpleLogger.log.test.FTPCommandTraceTest=trace

# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
#org.slf4j.simpleLogger.showDateTime=false
org.slf4j.simpleLogger.showDateTime=true

# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss:SSS Z

# Set to true if you want to output the current thread name.
# Defaults to true.
#org.slf4j.simpleLogger.showThreadName=true

# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true

# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false

実行結果例

FTPClientからFTPサーバへの要求は”>”、FTPサーバからFTPClientへの応答は”<“として、TRACEレベルでログを出力しています。

... [main] TRACE test.ExampleCommandListener - < 220 (vsFTPd 3.0.2)
...
... [main] TRACE test.ExampleCommandListener - > USER
... [main] TRACE test.ExampleCommandListener - < 331 Please specify the password.
...
... [main] TRACE test.ExampleCommandListener - > PASS
... [main] TRACE test.ExampleCommandListener - < 230 Login successful.
...
... [main] TRACE test.ExampleCommandListener - > SYST
... [main] TRACE test.ExampleCommandListener - < 215 UNIX Type: L8
...
... [main] TRACE test.ExampleCommandListener - > PORT
... [main] TRACE test.ExampleCommandListener - < 200 PORT command successful. Consider using PASV.
...
... [main] TRACE test.ExampleCommandListener - > LIST
... [main] TRACE test.ExampleCommandListener - < 150 Here comes the directory listing.
...
... [main] TRACE test.ExampleCommandListener - < 226 Directory send OK.
...
... [main] DEBUG test.FTPCommandTraceTest - name=test.txt
... [main] DEBUG test.FTPCommandTraceTest - name=work
... [main] TRACE test.ExampleCommandListener - > QUIT
... [main] TRACE test.ExampleCommandListener - < 221 Goodbye.


(adsbygoogle = window.adsbygoogle || []).push({});


(adsbygoogle = window.adsbygoogle || []).push({});

-1. システムエンジニアリング, Java

執筆者:

関連記事

ASP.NET Core: 日本語の文字化け

ダイジェスト Visual Studio 2019で作成したASP.NET Coreプロジェクトで、プログラム(Razor)から日本語を出力するとHTMLエンコードされてしまいます。例えば「さしすせそ …

PowerPointの削除できない個人情報を消す

PowerPointで「個人情報の削除」を実行するとこで、作成者や会社名等の個人情報を削除できます。しかしながら、特定の項目に入った個人情報については、PowerPointやWindowsの標準機能で …

ASP.NET Core: IHttpClientFactoryの使用方法

とりあえず、どんなサンプルになるか知りたい人は下記のサンプルをご覧ください。 ASP.NET Core: IHttpClientFactoryの単純サンプル ASP.NET Core: IHttpCl …

ftp, ftps, sftpの違い

開発対象システムの連携先システムとして、ftpsやらftpsサーバが指定される場合がある。 私の場合、開発標準の役割を担う場合が多く、これらの仕様を把握し、動作確認や単体テスト用のダミーのサーバを用意 …

DB操作フレームワーク はJPA or mybatis?

開発に向けた準備で、開発標準を準備するフレームワーク(FW)チーム、それらを使って実装を行う業務チームが集まって、「DB操作を行うためのFWは何を使うか?」という協議になった。 FWチームは、FW・J …