1. 概要

簡単に言えば、Spring Shell project は、コマンドを処理し、Springプログラミングモデルを使用してフル機能のCLIを構築するためのインタラクティブなシェルを提供します。

この記事では、その機能、キークラス、およびアノテーションについて説明し、いくつかのカスタムコマンドとカスタマイズを実装します。

2. Mavenの依存関係

まず、spring-shell依存関係をpom.xmlに追加する必要があります。

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell</artifactId>
    <version>1.2.0.RELEASE</version>
</dependency>

このアーティファクトの最新バージョンはここにあります。

3. シェルへのアクセス

アプリケーションでシェルにアクセスするには、主に2つの方法があります。

1つ目は、アプリケーションのエントリポイントでシェルをブートストラップし、ユーザーが次のコマンドを入力できるようにすることです。

public static void main(String[] args) throws IOException {
    Bootstrap.main(args);
}

2つ目は、 JLineShellComponent を取得し、プログラムでコマンドを実行することです。

Bootstrap bootstrap = new Bootstrap();
JLineShellComponent shell = bootstrap.getJLineShellComponent();
shell.executeCommand("help");

この記事の例に最も適しているため、最初のアプローチを使用しますが、ソースコードには、2番目の形式を使用するテストケースがあります。

4. コマンド

シェルには、 clear help exit など、すべてのCLIの標準機能を提供する組み込みコマンドがすでにいくつかあります。

カスタムコマンドは、CommandMarkerインターフェイスを実装するSpringコンポーネント内に@CliCommandアノテーションでマークされたメソッドを追加することで公開できます。

そのメソッドのすべての引数は、 @CliOption アノテーションでマークする必要があります。これを行わないと、コマンドを実行しようとしたときにいくつかのエラーが発生します。

4.1. シェルへのコマンドの追加

まず、コマンドがどこにあるかをシェルに知らせる必要があります。 このためには、ファイル META-INF / spring / spring-shell-plugin.xml がプロジェクトに存在する必要があります。そこで、Springのコンポーネントスキャン機能を使用できます。

<beans ... >
    <context:component-scan base-package="org.baeldung.shell.simple" />
</beans>

コンポーネントがSpringによって登録およびインスタンス化されると、それらはシェルパーサーに登録され、それらのアノテーションが処理されます。

2つの簡単なコマンドを作成してみましょう。1つはURLの内容を取得して表示するコマンドで、もう1つはそれらの内容をファイルに保存するコマンドです。

@Component
public class SimpleCLI implements CommandMarker {

    @CliCommand(value = { "web-get", "wg" })
    public String webGet(
      @CliOption(key = "url") String url) {
        return getContentsOfUrlAsString(url);
    }
    
    @CliCommand(value = { "web-save", "ws" })
    public String webSave(
      @CliOption(key = "url") String url,
      @CliOption(key = { "out", "file" }) String file) {
        String contents = getContentsOfUrlAsString(url);
        try (PrintWriter out = new PrintWriter(file)) {
            out.write(contents);
        }
        return "Done.";
    }
}

@CliCommandおよび@CliOptionvalueおよびkey属性にそれぞれ複数の文字列を渡すことができることに注意してください。これにより、次のことが可能になります。同じように動作するいくつかのコマンドと引数を公開します。

それでは、すべてが期待どおりに機能しているかどうかを確認しましょう。

spring-shell>web-get --url https://www.google.com
<!doctype html ... 
spring-shell>web-save --url https://www.google.com --out contents.txt
Done.

4.2. コマンドの可用性

boolean を返すメソッドで、 @CliAvailabilityIndicator アノテーションを使用して、実行時にコマンドをシェルに公開する必要があるかどうかを変更できます。

まず、web-saveコマンドの可用性を変更するメソッドを作成しましょう。

private boolean adminEnableExecuted = false;

@CliAvailabilityIndicator(value = "web-save")
public boolean isAdminEnabled() {
    return adminEnableExecuted;
}

次に、adminEnableExecuted変数を変更するコマンドを作成しましょう。

@CliCommand(value = "admin-enable")
public String adminEnable() {
    adminEnableExecuted = true;
    return "Admin commands enabled.";
}

最後に、それを確認しましょう:

spring-shell>web-save --url https://www.google.com --out contents.txt
Command 'web-save --url https://www.google.com --out contents.txt'
  was found but is not currently available
  (type 'help' then ENTER to learn about this command)
spring-shell>admin-enable
Admin commands enabled.
spring-shell>web-save --url https://www.google.com --out contents.txt
Done.

4.3. 必要な引数

デフォルトでは、すべてのコマンド引数はオプションです。 ただし、@CliOptionアノテーションのmandatory属性を使用して、これらを必須にすることができます。

@CliOption(key = { "out", "file" }, mandatory = true)

これで、導入しないとエラーが発生することをテストできます。

spring-shell>web-save --url https://www.google.com
You should specify option (--out) for this command

4.4. デフォルトの引数

@CliOptionの空のkey値は、その引数をデフォルトにします。 そこで、名前付き引数の一部ではない、シェルに導入された値を受け取ります。

@CliOption(key = { "", "url" })

それでは、期待どおりに機能することを確認しましょう。

spring-shell>web-get https://www.google.com
<!doctype html ...

4.5. ユーザーを支援する

@CliCommandおよび@CliOptionアノテーションは、組み込みのhelpコマンドまたはオートコンプリートを取得するためにタブを付けるとき。

web-get を変更して、カスタムヘルプメッセージを追加しましょう。

@CliCommand(
  // ...
  help = "Displays the contents of an URL")
public String webGet(
  @CliOption(
    // ...
    help = "URL whose contents will be displayed."
  ) String url) {
    // ...
}

これで、ユーザーはコマンドの機能を正確に知ることができます。

spring-shell>help web-get
Keyword:                    web-get
Keyword:                    wg
Description:                Displays the contents of a URL.
  Keyword:                  ** default **
  Keyword:                  url
    Help:                   URL whose contents will be displayed.
    Mandatory:              false
    Default if specified:   '__NULL__'
    Default if unspecified: '__NULL__'

* web-get - Displays the contents of a URL.
* wg - Displays the contents of a URL.

5. カスタマイズ

BannerProvider PromoptProvider HistoryFileNameProvider インターフェースを実装してシェルをカスタマイズする方法は3つあり、すべてデフォルトの実装がすでに提供されています。

また、 @Order アノテーションを使用して、プロバイダーがこれらの実装よりも優先されるようにする必要があります。

カスタマイズを開始するために、新しいバナーを作成しましょう。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleBannerProvider extends DefaultBannerProvider {

    public String getBanner() {
        StringBuffer buf = new StringBuffer();
        buf.append("=======================================")
            .append(OsUtils.LINE_SEPARATOR);
        buf.append("*          Baeldung Shell             *")
            .append(OsUtils.LINE_SEPARATOR);
        buf.append("=======================================")
            .append(OsUtils.LINE_SEPARATOR);
        buf.append("Version:")
            .append(this.getVersion());
        return buf.toString();
    }

    public String getVersion() {
        return "1.0.1";
    }

    public String getWelcomeMessage() {
        return "Welcome to Baeldung CLI";
    }

    public String getProviderName() {
        return "Baeldung Banner";
    }
}

バージョン番号とウェルカムメッセージも変更できることに注意してください。

それでは、プロンプトを変更しましょう。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimplePromptProvider extends DefaultPromptProvider {

    public String getPrompt() {
        return "baeldung-shell";
    }

    public String getProviderName() {
        return "Baeldung Prompt";
    }
}

最後に、履歴ファイルの名前を変更しましょう。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleHistoryFileNameProvider
  extends DefaultHistoryFileNameProvider {

    public String getHistoryFileName() {
        return "baeldung-shell.log";
    }

    public String getProviderName() {
        return "Baeldung History";
    }

}

履歴ファイルには、シェルで実行されたすべてのコマンドが記録され、アプリケーションと一緒に配置されます。

すべてが整ったら、シェルを呼び出して、動作を確認できます。

=======================================
*          Baeldung Shell             *
=======================================
Version:1.0.1
Welcome to Baeldung CLI
baeldung-shell>

6. コンバーター

これまで、コマンドの引数として単純型のみを使用してきました。 Integer Date Enum File などの一般的なタイプには、デフォルトのコンバーターがすでに登録されています。

Converter インターフェースを実装することで、カスタムオブジェクトを受信するためのコンバーターを追加することもできます。

StringURLに変換できるコンバーターを作成しましょう。

@Component
public class SimpleURLConverter implements Converter<URL> {

    public URL convertFromText(
      String value, Class<?> requiredType, String optionContext) {
        return new URL(value);
    }

    public boolean getAllPossibleValues(
      List<Completion> completions,
      Class<?> requiredType,
      String existingData,
      String optionContext,
      MethodTarget target) {
        return false;
    }

    public boolean supports(Class<?> requiredType, String optionContext) {
        return URL.class.isAssignableFrom(requiredType);
    }
}

最後に、web-getおよびweb-saveコマンドを変更してみましょう。

public String webSave(... URL url) {
    // ...
}

public String webSave(... URL url) {
    // ...
}

ご想像のとおり、コマンドは同じように動作します。

7. 結論

この記事では、SpringShellプロジェクトのコア機能について簡単に説明しました。 コマンドを提供し、プロバイダーを使用してシェルをカスタマイズすることができました。さまざまな実行時の条件に応じてコマンドの可用性を変更し、単純な型コンバーターを作成しました。

この記事の完全なソースコードは、GitHubにあります。