【Maven研究】Maven Pluginを自分で実装してみたらすごく理解が深まった!③

技術

色々試してみる

前回は初めてのプラグイン開発ということでGreetingMojoというログ出力するだけの何でもないプラグインを作ってみました。
今回はこのプラグインを色々いじってみながら、Mavenプラグインをもう少しだけ理解してみようと思います。

【Maven研究】Maven Pluginを自分で実装してみたらすごく理解が深まった!②
最初のプラグインを作る 前回はMavenプラグインの役割を確認し、プラグイン開発の環境をDockerコンテナに作成するところまで出来ました。 そんなわけで、今回はいよいよMaven本家のチュートリアルに従って実際にプラグインを作ってみます。...

まず、前回作ったGreetingMojoクラスの実装を確認してみます。

package sample.plugin;
 
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; 

/**
 * Says "Hi" to the user.
 *
 */
@Mojo(name = "sayhi", defaultPhase = LifecyclePhase.COMPILE)
public class GreetingMojo extends AbstractMojo
{
    /**
     * The greeting to display.
     */
    @Parameter(property = "sayhi.greeting", defaultValue = "Hello World!" )
    private String greeting;

    public void execute() throws MojoExecutionException
    {
        getLog().info("Hello, world. - " + greeting);
    }
}

このプラグインはビルド ライフサイクルの任意のフェーズに紐づけることができます。

たとえばdefaultライフサイクルの場合「validate~deploy」まで全部で23個のフェーズがあります。

Maven Core – Lifecycles Reference

試しにこの23フェーズの全てにsayhiゴールを紐づけてみます。
greetingプロパティにはそれぞれのフェーズ名を指定して出力内容が変わるようにしてみました。

    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>hello-validate</id>
            <phase>validate</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>validate</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-initialize</id>
            <phase>initialize</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>initialize</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-generate-sources</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>generate-sources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-sources</id>
            <phase>process-sources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-sources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-generate-resources</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>generate-resources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-resources</id>
            <phase>process-resources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-resources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-compile</id>
            <phase>compile</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>compile</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-classes</id>
            <phase>process-classes</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-classes</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-generate-test-sources</id>
            <phase>generate-test-sources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>generate-test-sources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-test-sources</id>
            <phase>process-test-sources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-test-sources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-generate-test-resources</id>
            <phase>generate-test-resources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>generate-test-resources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-test-resources</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-test-resources</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-test-compile</id>
            <phase>test-compile</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>test-compile</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-process-test-classes</id>
            <phase>process-test-classes</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>process-test-classes</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-test</id>
            <phase>test</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>test</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-prepare-package</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>prepare-package</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-package</id>
            <phase>package</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>package</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-pre-integration-test</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>pre-integration-test</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-integration-test</id>
            <phase>integration-test</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>integration-test</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-post-integration-test</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>post-integration-test</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-verify</id>
            <phase>verify</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>verify</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-install</id>
            <phase>install</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>install</greeting>
            </configuration>
          </execution>
          <execution>
            <id>hello-deploy</id>
            <phase>deploy</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>deploy</greeting>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>

このプロジェクトでたとえばmvn installのようにコマンドを実行すると「Hello, world. – validate」から順に出力されて「Hello, world. – install」まで出力されます。

deployフェーズはinstallフェーズの後なので「Hello, world. – deploy」とは出力されないことがわかると思います。

skipTests=trueでtestフェーズはスキップされる?

試してみましょう。

先ほど試した通りdefaultライフサイクルの全てのフェーズとsayhiゴールが紐づけられた状態でmaven installします。
この時、-DskipTests=trueを付けて実行することで単体テストの実行が通常はスキップされます。

mvn install -DskipTests=true

もし、単体テストのスキップはtestフェーズ自体がスキップされているなら、当然testフェーズに紐づけたsayhiゴールも実行されないはずです。

実行した結果は下記のとおりです。
(全体の結果は長すぎるので、testフェーズの部分だけ抜粋しました)

見ての通り、testフェーズに紐づけたsayhiゴールも実行されています。

考えてみれば当然ですね。
よく見るとmaven-surfire-pluginのtestゴールも実行されたうえで「Tests are skipped.」と出力されています。

testフェーズ自体がスキップされるわけではなく、testフェーズと紐づけられることを想定しているゴールがskipTestsというシステム プロパティを参照してテストの実行をスキップするように実装されているだけだということです。

ではsayhiゴールでも「skipHello」というシステム プロパティによって出力内容を切り替えるように改造してみましょう。

システム プロパティを参照する

前回実装したGreetingMojoにはgreetingというプロパティがすでに定義されています。

プラグインに後から値を設定する場合、@Parameterアノテーションを使ってプロパティを定義するのでした。

システム プロパティを受け取る時もプロパティを使います。

package sample.plugin;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; 

/**
 * Says "Hi" to the user.
 *
 */
@Mojo(name = "sayhi", defaultPhase = LifecyclePhase.COMPILE)
public class GreetingMojo extends AbstractMojo
{
    /**
     * The greeting to display.
     */
    @Parameter(property = "sayhi.greeting", defaultValue = "Hello World!" )
    private String greeting;

    /**
     * The settings of skipping this goal.
     */
    @Parameter(property = "sayhi.skipHello", defaultValue = "${skipHello}")
    private Boolean skipHello = false;

    public void execute() throws MojoExecutionException
    {
        if (skipHello == true) {
            getLog().info("sayhi skipped.");
        }  else {
            getLog().info("Hello, world. - " + greeting);
        }
    }
}

22~26行目にskipHelloというプロパティを定義しました。

@Parameterではプロパティ名を「sayhi.skipHello」、デフォルト値を「${skipHello}」としています。
このデフォルト値定義がシステム プロパティを受け取る定義です。

つまりsayhi.skipHelloに直接プロパティが定義される時はその値を、省略される時はシステム プロパティのskipHelloの値をデフォルト値として採用するということです。

ちなみに、どちらも省略されてしまった場合は「private Boolean skipHello = false;」と実装しているためfalseで初期化されることになります。

あとはexecuteメソッドでこのskipHelloプロパティを参照して出力内容を切り替えるように実装を修正するだけです。

このskipHelloプロパティに値を設定する方法はいくつかあります。

skipHello設定方法1:sayhi.skipHello

前回、hello-maven-pluginの動作確認のために作ったhello-worldプロジェクトのpom.xmlを下記のように修正します。

    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>compile</greeting>
              <skipHello>true</skipHello>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>

つまり、greetingプロパティと同様に<configuration>タグを使って設定する方法です。

skipHello設定方法2:skipHelloシステムプロパティ①

システムプロパティの設定方法にもいくつかあります。

まずは<properties>タグで設定する方法。
pom.xmlを下記のように修正します。

<?xml version="1.0" encoding="UTF-8"?>

<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>sample.plugin</groupId>
  <artifactId>hello-world</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>hello-world</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>
  <packaging>jar</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <skipHello>true</skipHello>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
          ・
          ・
        (中略)
          ・
          ・
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>sayhi</goal>
            </goals>
            <configuration>
              <greeting>compile</greeting>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

<configuration>タグの方の定義は消しておきます。
消し忘れると<properties>タグで指定した設定は無視されてしまいます。

skipHello設定方法3:skipHelloシステムプロパティ②

mvnコマンドの実行時にskipHelloを指定することもできます。

実行時に下記のようにコマンドを打つだけです。

mvn compile -DskipHello=true

上の例では「compile」をターゲットのフェーズとしていますが、「test」や「install」など、どのフェーズでも構いません。

ポイントは-DskipHello=trueというオプション指定の部分です。
これによってシステムプロパティ「skipHello」に設定することができます。

ちなみに<properties>タグの指定よりコマンドで指定したほうが優先されます。

まとめ

前々回前回と今回の全3回に渡ってMavenプラグインについて調べてみました。

実際のところ、今回調べたことはほとんどMavenの公式サイトに記載されている事だったり、もっとわかりやすくまとめられている記事がたくさんあります。

ただ、やっぱり自分の手で実装したり試したりすることは大事だなと改めて思いました。
実際に自分で試したうえで記事を読むと今まで以上に理解が深まるし、記憶にも定着しやすいと思います。

そんなわけで、今回のシリーズはいい勉強になりました。

【Maven研究】という意味では他にも依存関係の辺りを調べてみたり、よく使うプラグイン、例えばmaven-shade-pluginやmaven-dependency-pluginといったプラグインについて調べてみたりしてもいいかもしれません。
また、ライフサイクル拡張とかっていうのもできそうなので、機会があれば調べてみたいなと思います。

では、今回はこの辺りで終わろうと思います。


【Maven研究】プラグイン開発 シリーズ目次

  1. 【Maven研究】Maven Pluginを自分で実装してみたらすごく理解が深まった!①
  2. 【Maven研究】Maven Pluginを自分で実装してみたらすごく理解が深まった!②
  3. 【Maven研究】Maven Pluginを自分で実装してみたらすごく理解が深まった!③

コメント

タイトルとURLをコピーしました