CodeZine(コードジン)

特集ページ一覧

Jakarta Commonsを使ってJDKクラスを拡張する:パート3

Commons CLI/VFS/Configuration/Poolの利用

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/02/14 12:30

今回は、Jakarta Commonsのなかでも、コマンドラインアプリケーションの引数の解析、各種ファイルシステムへの同時接続、多様なソースからもたらされる設定情報への統一的アクセス、任意のオブジェクトのプールといった処理を可能にするコンポーネントを扱います。

目次

はじめに

 Jakarta Commonsは、さまざまなJakartaプロジェクトで使われている再利用可能なクラスの集まりです。これらのクラスは、独立したコンポーネントとして自分のJavaプロジェクトで利用することができます。今回はJakarta Commonsを紹介する3回シリーズの最終回にあたり、便利なコンポーネントをあと4つ取り上げ、サンプルアプリケーションを通じてその使い方を説明していきます。まだパート1パート2を読んでいない方は、先にそちらをご覧ください。これらのサンプルはJakarta Commonsコンポーネントを例示するだけのものではなく、典型的なJavaプロジェクトで再利用できる有用な機能を盛り込んだ完全なアプリケーションです。

 本稿では次のコンポーネントを取り上げます。

  • CLI(コマンドラインインターフェイス)
  • VFS(仮想ファイルシステム)
  • Configuration
  • Pool

 本稿には完全なソースコードが付属しており、各サンプルのテストケースをJUnitで起動することで実行できます。

著者注
 Commonsコンポーネントのアーキテクチャと本稿で紹介するサンプルを理解するためには、オブジェクト指向プログラミング(OOP)とGang of Fourのデザインパターン(ChainおよびCommand)についての基本的知識が非常に役立ちます。

CLI

 CLI(コマンドラインインターフェイス)コンポーネントは、コマンドラインアプリケーションの引数解析に大変重宝します。こうした引数解析コードを書く作業は、時間がかかって煩わしいものです。また、このコンポーネントを使えば、既存のCLIアプリケーションの機能拡張も容易になります。コードのリファクタリングにより、新しい機能を効率的に追加できます(Jakartaのサイトには、CLIの使い方の概要がわかりやすく記されています)。

 本シリーズの第1回では、Commons Chainコンポーネントの解説のところで、ネットワークコマンドを実行するコマンドラインツールのサンプルを使用しました。Common CLIの説明にも同じサンプルアプリケーションを使います。このサンプルのソースコードは、ダウンロードサンプルのパッケージ「in.co.narayanan.commons.cli」に含まれています。メインクラスCommandLineを起動すれば、このサンプルアプリケーションの引数が確認できます。リスト1は、このサンプルアプリケーションのコマンドラインオプションです。

リスト1 サンプルアプリケーションで使用できるコマンドオプションの構文
// java CommandLine -user admin -password manager -ping {host}
// java CommandLine -user admin -password manager
//   -ftp {host} -get {path_to_file}
// java CommandLine -user admin -password manager
//   -ftp {host} -ls {path_to_file}

 このコードの処理レイヤには、CommonsのChainとCommandのパターンが使われています。コマンドラインのネットワークコマンドのそれぞれに、該当する処理を行うクラスが存在します。各クラスは連結してチェーンを形成します。コマンドが呼び出されると、まずチェーンの最初のコマンドに引数が与えられます。目的の処理を行うコマンドが見つかるまで、このチェーンに沿って実行が進みます。ここでの目標は、チェーン内の処理クラスがコンテキストオブジェクトを利用できるように、Commons CLIを使って引数の解析とコンテキストオブジェクトの作成を行うことです。

 リスト2は、「in.co.narayanan.commons.cli.CommandLine.java」のコードの一部です。pingコマンドに渡された引数をCommons CLIを用いて解析する方法を示しています。

リスト 2 Commons CLIを利用したpingコマンドの引数解析
private void createPingCmdOptions() {
    // ping command
    // java CommandProcessor -user admin -password manager -ping {host}

    pingOptions = new Options();

    Option user = createUserOption();
    pingOptions.addOption(user);

    Option passwd = createPasswordOption();
    pingOptions.addOption(passwd);

    Option ping = OptionBuilder.withArgName("ping")
                              .hasArg()
                               .isRequired()
                               .withDescription("Ping a remote system")
                               .create("ping");
    pingOptions.addOption(ping);
}

 このコードでは、個々のコマンドラインオプションを表すOptionクラスのインスタンスを作成して、Optionsクラスのインスタンスに追加していきます。OptionBuilderクラスは、一連の静的メソッドを通じてOptionクラスのインスタンスを作成する機能を持ちます。リスト2では、pingという名前の、引数を1つ取る必須オプションのインスタンスを作成しています。このインスタンスには、オプションの説明("Ping a remote system")も含まれています。OptionBuilderwithArgNameメソッドに渡される文字列は、解析終了後にその引数の値を取り出すために使用されます(具体的な方法は後述)。OptionBuilderを使わずにOptionクラスのインスタンスを直接作成して、オプションのもっと細かい設定をすることもできます。

 createUserOptionメソッドとcreatePasswordOptionメソッドは、それぞれコマンドラインのuser引数とpassword引数を表す新しいインスタンスを返します。今回のサンプルでは、別のコマンド用にOptionクラスのインスタンスを作成するときに、これらのメソッドを使用します。

 リスト3は、ftpコマンド用のCommons CLIのコードです。

リスト3 Commons CLIを利用したftpコマンドの引数解析
Option ftp = OptionBuilder.withArgName("ftp")
    .hasArg()
    .isRequired()
    .withDescription("File transfer protocol")
    .create("ftp");
ftpOptions.addOption(ftp);

// For additional ftp commands like ls, put, and mget,
// a OptionGroup needs to be created
// to indicate the options are mutually exclusive
Option get = OptionBuilder.withArgName("get")
    .hasArg()
    .withDescription("Get a file from the server")
    .create("get");

Option ls = OptionBuilder.withArgName("ls")
    .hasArg()
    .withDescription("List the folder contents in the server")
    .create("ls");
OptionGroup ftpArgs = new OptionGroup();
ftpArgs.addOption(get);
ftpArgs.addOption(ls);
ftpArgs.setRequired(true);
ftpOptions.addOptionGroup(ftpArgs);

 このサンプルで扱うftpネットワークコマンドには、getとlsという相互排他的なオプションが含まれます。こうした相互排他的なオプションを表すために、リスト3ではOptionGroupクラスを使用しています。具体的には、OptionGroupクラスのsetRequiredメソッドを呼び出すときに、これらの相互排他的オプションが必須であることを示すtrueを渡しています。これにより、ユーザーはgetとlsのどちらのオプションを必ず入力しなければならなくなります。

 このコード全体において、Common CLIのフレームワークは次の処理を行っています。

  • コマンドライン引数の文字列配列を解析し、渡された値に基づいて各種Optionクラスのインスタンスを作成する
  • 検証を実施する(たとえば、必須の引数がユーザーから与えられなかった場合は例外をスローする)
  • ユーザーから与えられた引数の数や種類が正しくない場合は用法メッセージを表示する

 リスト4は、パーサークラスのparseメソッドを呼び出す例です。

リスト4 パーサーによる解析の実行
public void process(String args[]) {

    // remaining code

    CommandLineParser parser = new BasicParser();
    org.apache.commons.cli.CommandLine line = null;
    Context chainContext = null;

    // remaining code
        case PING : {
            try {
                line = parser.parse(pingOptions, args);
                chainContext = getPingContext(line);
            } catch (ParseException e) {
                System.out.println(e.toString());
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp(
                    "Ping options", pingOptions);
            }
        } break;
    // remaining code
}

private Context getPingContext(
    org.apache.commons.cli.CommandLine line) {
    String user = line.getOptionValue("user");
    String passwd = line.getOptionValue("password");
    String host = line.getOptionValue("ping");
    return new CommandlineContext(
        user, passwd, new CLICommand("-ping", new String[] {host}));
}

 太字のコードは、パーサーの使い方を理解するにあたって非常に重要な部分です。最初に、パーサークラスBasicParserのインスタンスを作成しています。なお、Unix形式のCLIコマンドの場合はPosixParserを使用します。カスタムの要件によっては、org.apache.commons.cli.Parserクラスの拡張、またはorg.apache.commons.cli.CommandlineParserインターフェイスの実装を行うことでパーサーを一から作ることもできます。

 parseメソッドを呼び出すと、コマンドラインオプションの値を取得するために利用するorg.apache.commons.cli.CommandLineへの参照が返されます。ここでは、この参照をgetPingContextメソッドに渡すことで、個々のオプション値を含んだCommandlineContextクラスのインスタンスを作成しています。このインスタンスを、以降の処理のためにチェーンに渡します。

 parseメソッドによる引数解析の途中で問題が生じた場合は、Commons CLIは例外をスローします。この場合は、org.apache.commons.cli.HelpFormatterクラスによって、エラーを通知する書式設定済みのヘルプメッセージが表示されます。

 Commons CLIのフレームワークは、単独のコマンドに対してのみオプションの解析を行うことができます。このサンプルと同じように、コマンドオプションのあるコマンドが複数存在するCLIアプリケーションの場合は、コマンドの種類を識別するために少し前処理を行う必要があります(リスト5を参照)。

リスト5 コマンドの識別
private int classifyCommand(String args[])
    throws CommandLineException {
    if(args != null && args.length > 0) {
        for(String arg : args) {
            if(arg.equals("-ping")) {
                return PING;
            }

            if(arg.equals("-ftp")) {
                return FTP;
            }
        }
    } else {
        throw new CommandLineException(
            "Invalid command options. See usage.");
    }
    throw new CommandLineException(
        "Invalid command options. See usage");
}

 このサンプルアプリケーションでは、pingとftpは異なるコマンドであり、それぞれ異なるコマンドラインオプションを持っています。そのため、Optionsクラスのインスタンスを1つ用意しただけでは、これらのコマンドライン引数を表現できません。コマンドの種類を識別したうえで、以降の解析に適切なOptionsクラスのインスタンスを用いることになります。

 Commons CLIはJavaのあらゆるCLIアプリケーションにとって不可欠なすばらしいAPIです このAPIを使えば、CLIアプリケーションの機能拡張にかかる時間を節約し、煩わしさを軽減できます。Jakarta Antプロジェクトでは、コマンドライン引数の処理にCommons CLIが利用されています。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • Narayanan A.R.(Narayanan A.R.)

    テスト駆動開発、アジャイル方法論、Javaテクノロジ、およびデザインパターンの熱烈な擁護者。Javaテクノロジを使ったソフトウェアのデザインおよび開発に携わって数年になる。

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5