Java CLI ツールのスケルトン

知り合いの Java 開発者が、コマンドラインで使う CLI tool は開発したことがない、と言っていました。なのでその知り合いのため、Java 用に簡単なテンプレートを作成してみました。

他の方にあまり参考にはならないとおもいますが、いちおう公開しておきますね。GitHub リポジトリは [yamachan/CLIToolBase](https://github.com/yamachan/CLIToolBase) です。

## 環境

環境は以前に [投稿した](https://qiita.com/yamachan360/items/a9e7c5ec39f0354544bd) 時からあまり更新しておらず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/bf97728f-cfaa-14db-56b8-cc876fd7939d.png)
開発環境はレトロな [Eclipse IDE 2020-09 for Enterprise Java Developers](https://www.eclipse.org/downloads/packages/release/2020-09/r) です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/98422647-75e1-c3d1-09d7-e2a5133896a2.png)

## プロジェクトの新規作成

ファイルの New メニューから Java Prokect を新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/f3fa5bdc-cc7b-2238-3868-d696d8436047.png)
Project Name を適当に入力 (今回は CLIToolBase) して Finish で終了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/d6f66688-f3e1-da55-b50f-f60bbe10c89c.png)
Package Explorer に作成したパッケージが表示されていればok。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/314560eb-1178-e585-7209-985b841ac1dd.png)

## git init しておく

まだ空のプロジェクトですが、いったん git init を実行して初期化しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/38aa0238-7459-9194-8da4-c0b98ddc048c.png)
この空の状態を [first commit](https://github.com/yamachan/CLIToolBase/commit/d5ddf5800c2023b3d3b4d80dc31ed3931e5204e2) として　push しておきました。

## main 関数を作成

さて、まずは最初に実行されるクラスを新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/52b540cc-a6ff-2def-7744-398f0682ee62.png)
適当なクラス名 (今回は CLIMain) を指定し、main 関数の生成をチェックして Finish。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/7df89ade-2a60-0f6c-80d2-b49602487b96.png)
以下のような感じで生成されますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/e7de062e-ade9-e563-7708-39e80744cc8b.png)
とりあえず、定番の1行を追加してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/85527143-12dc-b591-e7b5-223071b4fe84.png)
変更を保存したら、クラスファイルを右クリックして `Run As Java Application` です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/2a8329a7-eda6-ee3f-8bea-4188472dba0e.png)
コンソールに `Hello World.` が表示されればok。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9ba42212-0d14-6433-3525-5f2b72a1df29.png)
ついでに簡単な README.md ファイルを追加して [push](https://github.com/yamachan/CLIToolBase/commit/ab24702aef96504f5b3918dcc993aed0956be458) してみました。

## Apache Commons CLI の導入

CLI tool を書く場合は必須ともいえる [Apache Commons CLI](https://commons.apache.org/proper/commons-cli/) を導入します。

ちょっと原始的ですが [mvnrepository](https://mvnrepository.com/artifact/commons-cli/commons-cli) などから好みのバージョンをダウンロードします。私は新しめの [commons-cli-1.5.0.jar](https://mvnrepository.com/artifact/commons-cli/commons-cli/1.5.0) ファイルをダウンロードしました。ダウンロード先はどこでも良い気はしますが、今回はプロジェクト内に `lib` フォルダを作成して、配置しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/4e5a1765-bf17-ff24-ce6a-960f29e71707.png)
そして `Build Path` メニューにある `Configure Build Path...` メニューから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9042f86b-8129-f404-d9ee-06901aeb55c1.png)
`Add JARs...` メニューから、配置した jar ファイルを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/a11f1475-6964-fb76-c73f-d3ac39fe3075.png)
こんな感じに追加されていればok。`Apply and Close` で閉じましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/0d75b674-f16b-937a-8a05-ab19f675d2d7.png)

## 動作確認

[Using Apache Commons CLI](https://commons.apache.org/proper/commons-cli/usage.html) にあるサンプルコードを適当にコピーして動作確認してみます。

```java:CLIMain.java
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class CLIMain {

	public static void main(String[] args) throws ParseException {
		// create Options object
		Options options = new Options();

		// add t option
		options.addOption("t", false, "display current time");
		
		CommandLineParser parser = new DefaultParser();
		CommandLine cmd = parser.parse(options, args);

		if(cmd.hasOption("t")) {
			System.out.println("Sample output with t option.");		
		} else {
			System.out.println("Sample output without t option.");		
		}
	}
}
```

このコードを単に実行すると else 文のほうのメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/bb7aba93-4e9e-e680-ea78-777a33583d2f.png)
実行の `Run Configuration...` から、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/94290eeb-a4d3-49fc-213c-f22f00995947.png)
パラメータ部分に `-t` オプションを指定して実行すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/8d0815d0-6b3c-e479-19c4-718083c6880a.png)
if文の `cmd.hasOption("t")` が成立して、表示メッセージが変わることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/6f80cfc8-89b0-7f1c-21fa-6541df2ba781.png)
この変更も [push](https://github.com/yamachan/CLIToolBase/commit/a657e695bad45748a958c07e7b9f7c027fff3c5d) してみました。

## 長いオプション名も有効にする

`-t` に加えて `--time` という長いオプション名を使えると良いですよね。むろん今は実行エラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/de5b2891-d980-e3b9-614c-d90384903a0f.png)
指定できるようにするには、以下のように変更します。

```java:CLIMain.java
		// add t option
		options.addOption("t", "time", false, "display current time");
```

メッセージも少し修正したものを [push](https://github.com/yamachan/CLIToolBase/commit/1fbba0d85f68ea347096284ec15e5b999836594c) しましたので、参照してみてください。

## Help メッセージの生成

commons-cli ライブラリで私が一番好きなのは、ヘルプメッセージの生成が楽になるところです。サンプルを以下のように拡張してみました。

```java:CLIMain.java
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class CLIMain {
	public static String version = "1.0.0";
	public static String update = "2023/05/07";
	public static void main(String[] args) throws ParseException {
		// create Options object
		Options options = new Options();

		// add options
		options.addOption("t", "time", false, "display current time");
		options.addOption("h", "help", false, "display help message");
		
		CommandLineParser parser = new DefaultParser();
		CommandLine cmd = parser.parse(options, args);

		if(cmd.hasOption("h")) {
			HelpFormatter hf = new HelpFormatter();
        	hf.printHelp("CLIToolBase", "A simple template repo for Java CLI tool.", options, "\nversion " + version + " at " + update, true);
           	System.exit(0);
		} else if (cmd.hasOption("t")) {
			System.out.println("Sample output with [-t|--time] option.");
		} else {
			System.out.println("Sample output without [-t|--time] option.");		
		}
	}
}
```

差分は [push](https://github.com/yamachan/CLIToolBase/commit/d00344b5a182fbd447aee4d460c4ae458bfaecd8) したものを参照したほうが良いかもしれません。

で、`-h` もしくは `--help` オプションを指定して実行すると、以下のようなヘルプメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/7fdd5be2-2a0e-62e9-5000-406e6054bb36.png)
パラメータ一覧の部分が自動生成なのがポイントです。

## ツールを配布する

作成したツールは jar 形式にまとめて利用者に配布しましょう。まず `Run Configurations` のパラメータ指定はいったん消しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/2d34f00a-5f15-ca69-3f4d-53eabe8854a5.png)
プロジェクトを右クリックして `Export...` を実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/8f8e70ac-4879-e7b3-2554-51e679ee3913.png)
`Runable JAR file` を選んで Next。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/be3073e2-d973-21ff-dce8-a79cbbb8a11e.png)
`Launch configuration` には `Run Configurations` と同じものを指定します。 `Export destination` で配布用の jar ファイル名を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/09a82cee-6f57-745d-0ba9-aceb129f7962.png)
`Library handling` は真ん中の選択肢をお勧めします。生成された jar の中に、実行に必要なライブラリもそのまま含まれるので、便利かつ楽です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/cfcc358c-346f-521a-3914-300bb84968ad.png)
※ 利用するライブラリのサイズが大きくて配布が面倒な場合は一番下を選んでファイルを分けましょう

Finish で jar ファイルが生成されますので、利用者に配布しましょう。jar ファイルを受け取った側は、以下のように実行できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9b100c7b-606e-0757-603b-cd5dcd701f86.png)

## というわけで

非常に初歩的な内容となりましたが、Java で CLI tool を書いてみる際の簡易テンプレートを作成し、ざっくり説明してみました。commons-cli ライブラリを使うと、CLI tool の UI 作成部分が非常に楽なので、自分に便利なものを量産すると良いとおもいますよ。

ではでは。