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

技術

分かるようでよく分かってないPOM

たまたま業務の都合で近頃はJava開発が多いです。
Spring Frameworkを使ったWebアプリケーション開発だったり、AWSのLambda開発だったり。

で、そのいずれでも現場の都合(というか好み)でビルドツールにはMavenを使っています。

ビルドツールって、他にもGradleとかAntとかあるけど、業務ではたぶんMavenを一番見かける気がします。

Mavenを使うということは、当然pom.xmlに依存関係やプラグインを追加したりする必要があるんだけど、正直なところPOMって何となくしか分かってないんですよね。

調べてみると、色々と解説してくれているページが見つかるんだけど・・・。
読んでるだけでは分かるようで、なんだかよくわからない。
やっぱり自分で手を動かして作ってみて初めて理解できるものだよね。

ってことで、Maven本家のチュートリアルを見ながらMavenプラグインを作ってみようと思います。

Maven – Plugin Developers Centre

実際にプラグインを作成するのは次回にして、今回はMavenとは何か、そしてMavenプラグインの役割について、調べてみました。

そもそもMavenって何?

Mavenの本家のサイトにはMavenの目的についてこのように書いています。

  • ビルドプロセスを簡単にすること(Making the build process easy)
  • 統一されたビルドシステムの提供(Providing a uniform build system)
  • 質の高いプロジェクト情報の提供(Providing quality project information)
  • より良い開発手法の奨励(Encouraging better development practices)

正直なところ、これだけ読んでも何が何やらさっぱりわからない。
ただ、要はこういうことなのかなと私は雑に理解しました。

  • ビルドするのが簡単になりますよ
  • そしてそれを人と共有するのも簡単ですよ

また、本家サイトの別ページにはこうも書かれています。

  • Mavenとは(その中心にあるものは)プラグイン実行フレームワークであり、すべての仕事がプラグインによって行われる。
    (Maven is – at its heart – a plugin execution framework; all work is done by plugins.)

つまりMavenはプラグインを実行するためのツールとして存在しているだけであり、ビルドにしても何にしても、すべての作業はプラグインに実装されているということらしいです。

では、プラグイン実行フレームワークとしてのMavenとは何なのか?

またまた本家のサイトからの引用ですが、このように書かれています。

  • Mavenはビルド ライフサイクルという中止的な概念がベースになっている。それはつまりが成果物(プロジェクト)をビルドし、配布するプロセスが明確に定義されているということだ。
    (Maven is based around the central concept of a build lifecycle. What this means is that the process for building and distributing a particular artifact (project) is clearly defined.)

私なりに要約すると、ビルド手順を定義した「ビルド ライフサイクル」というものがあって、Mavenはこの手順通りに処理を実行するツールだということなのかなと思います。

本家のサイトに書いてあるように、ビルド ライフサイクルという概念がMavenの中心的な位置にあるのなら、このビルド ライフサイクルをまずは理解する必要があるってことになりますね。

ビルド ライフサイクル

ビルド ライフサイクルの実体はビルド フェーズというものリストで、このビルド フェーズがいわゆるビルドの手順1つ1つということみたいです。

ビルド ライフサイクルはビルド フェーズのリストを上から順番に1つずつ実行することでビルドを実現するわけです。

Mavenに定義されているビルド ライフサイクルはこの3つ。

ライフサイクル説明
cleanビルドの成果物を削除してクリーンアップする
defaultビルドからテスト、パッケージング、デプロイまで実施する
siteプロジェクトのサイトドキュメントを作成する

例えば、defaultライフサイクルの場合ビルド フェーズは下記のようなもので構成されています。

defaultライフサイクルのフェーズ

フェーズ説明
validateプロジェクトが正しく、必要な情報がすべて利用可能であることを検証する
compileプロジェクトのソースコードをコンパイルする
test適切な単体テストフレームワークを使ってテストを実行する。ここでのテストではコードのパッケージ化やデプロイは必要ありません。
packageコンパイルされたコードを取得して、例えばJARなどの配布可能な形式にパッケージ化する
verify作成されたパッケージが有効で品質基準を見たいしていることを確認する
installパッケージをローカルリポジトリにインストールして、ローカルの他のプロジェクトから使えるようにする
deployパッケージをリモートリポジトリにコピーして、他の開発者や他のプロジェクトと共有できるようにする

厳密にいえば、defaultライフサイクルはもう少し多くのフェーズで構成されていますが、おおむねこの手順でビルドは実行されます。
このリストを上から順番に実行することでビルドが実現されるわけです。
※完全なフェーズのリストは「Lifecycle Reference」で確認してください。

ビルドを実行する時はこのビルド フェーズを指定することになります。
必要なのはビルド ライフサイクルではなくビルド フェーズを指定することです。

この時、指定するフェーズはライフサイクルを先頭から順に実行して、「ここまで実行したい」というフェーズです。

たとえば、具体的にはこんなコマンドになります。

mvn install

このコマンドはdefaultライフサイクルをinstallフェーズまで実行するという意味になります。
つまり、defaultライフサイクルの中からvalidatecompiletestpackageverifyinstallの順番で実行されることになります。
deployフェーズはinstallフェーズより後のフェーズなので実行されません。

このようにしてMavenはビルドプロセスを定義し、その実行を制御しています。

ところで、このビルド ライフサイクルおよびビルド フェーズはビルドの手順を定義しているだけで実際のビルド方法についての実装を定義しているわけではありません。

先にも触れたとおり、Mavenで行われるすべての仕事はプラグインに実装されています。

Mavenプラグインの役割

というわけでようやく本題のプラグインの話。

繰り返しになりますが、jarファイルやwarファイルを作成したり、コンパイルや単体テストを実行したりという、Mavenが行う具体的なアクションは全てプラグインに実装されています。
つまりプラグインはMavenの中心的な機能という位置づけになります。

ビルドの手順をビルド ライフサイクルというフレームワークとして定義し、各手続きの具体的な実装はプラグインに分離しているということです。
こうすることで、プラグインを入れ替えてビルドの作業を自由にカスタマイズできる仕組みになっているってわけ。

プラグインに実装されている具体的なアクションは「ゴール」と呼ばれます。
プラグインは任意の数のゴールで構成され、これらのゴールをビルド ライフサイクルのビルド フェーズと紐づけることで、ビルドの作業に組み込むことができます。

ゴールとビルド フェーズの紐づけ方法は2種類あって、1つは実装時にデフォルトの紐づけとして定義する方法。
もう1つはpom.xml上で紐づけを定義する方法です。

実装時に定義する方法は後で(次回)紹介するとして、ここではpom.xml上で定義する方法を紹介します。

例えば、下記は公式のページで例示されている定義です。

 <plugin>
   <groupId>com.mycompany.example</groupId>
   <artifactId>display-maven-plugin</artifactId>
   <version>1.0</version>
   <executions>
     <execution>
       <phase>process-test-resources</phase>
       <goals>
         <goal>time</goal>
       </goals>
     </execution>
   </executions>
 </plugin>

display-maven-pluginというプラグインにはtimeというゴールが定義されていて、これをprocess-test-resourcesというビルド フェーズと紐づけています。

process-test-resourcesというのはdefaultライフサイクルに定義されているビルド フェーズです。
つまり、例えばmvn packagemvn installなどのコマンドでdefaultライフサイクルが実行されると途中のprocess-test-resourcesが実行されるタイミングでtimeというゴールを実行させることができるのです。

プラグイン開発の環境を作る

説明を読むばかりではつまらないし、わかったようでわからないままなので、そろそろ実際にプラグインを作る作業に入っていきましょう。

プラグインはJavaで作るのでJavaとMavenがインストールされている環境が必要です。

いつも通りDockerコンテナに作ることにします。

# Debian 12 をベースイメージにする
FROM debian:12

# Java 17をインストール
RUN apt update && apt install -y openjdk-17-jdk

# Mavenをインストール
RUN apt install -y maven

このDockerfileを保存したディレクトリでビルドコマンドを実行

docker build -t mvnplugin .

そして起動

docker run -itd --name plugin-dev mvnplugin

VSCodeのRemote Development拡張を使って作成したplugin-devコンテナにアクセスすればOK。
念のため、JavaとMavenのバージョンを確認しておきます。

plugin-devコンテナにアクセスしているVSCodeで「Ctrl + @」を入力してTerminalを開き、下記のコマンドでバージョンを確認します。
まずはJava。

java --version
openjdk 17.0.11 2024-04-16
OpenJDK Runtime Environment (build 17.0.11+9-Debian-1deb12u1)
OpenJDK 64-Bit Server VM (build 17.0.11+9-Debian-1deb12u1, mixed mode, sharing)

続いて、Maven。

mvn --version
Apache Maven 3.8.7
Maven home: /usr/share/maven
Java version: 17.0.11, vendor: Debian, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "5.15.153.1-microsoft-standard-wsl2", arch: "amd64", family: "unix"

JavaはOpenJDK の 17.0.11、Mavenは 3.8.7 がインストールされていることが確認できました。
これで、プラグインを開発する環境はできました。

それでは次回、早速実装に着手したいと思います。

コメント

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