4
3

Apache Maven 再入門

Last updated at Posted at 2024-02-21

Zenn にも書籍として公開しています.
読みやすい方で読み進めていただけたらと思います.
Zenn の方が最新版です.

この記事の目的

入社当時に簡単にしか勉強していなかった Java 開発周りの知識を確かにするため,自分で Apache Maven のドキュメントを読んで動作確認 & まとめます.

読み返しやすいように,公式ドキュメントと記載順を若干変えて補足しています.

こちらの記事を参考にして Apache Maven を活用した開発を行うことができます.

こちらは公式ドキュメントをさらって記録に残した記事です.
Apache Maven の Tips 等に関する記事は既に世の中に多々ありますので,必要に応じて調べてみてください.
また,Apache Maven プロジェクトのアーキテクチャ1やコミュニティ2,コントリビュート手順3などはこちらの記事からは省きます.

Apache Maven とは

maven-logo.png

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.4

Apache Maven は Apache Software Foundation (ASF) が管理する Java ベースのプロジェクト管理 OSS ツールです.

Maven ではプロジェクト構成や情報をプロジェクトオブジェクトモデル (POM, pom.xml) に記載します.
Maven を使うことで POM と (今や標準規格となっている) 特有のプロジェクトレイアウトを利用して,ワークフロー・ビルドライフサイクルを活用できるようになります.
そのため,Maven を使った Java プロジェクトはベストプラクティスに沿って準備されていることが重要になります.

現在では Maven の利用は広がっており,Java プロジェクトを理解できるようになりたい場合は,まずとある一つのプロジェクトで pom.xml を読めるようになるのが良いかと思います.

Maven の強みは (推移的) 依存関係の管理にあり,任意のリリースバージョンの差し替えを (容易とまでは言いませんが) 強力にサポートします.
Git 等を用いることでコードとしてバージョン管理や変更差分の抽出,配布を補助することもできます.

また Java やスクリプト言語を利用したプラグインを開発・適用する方法も提供しています.
実は mvn コマンドで利用している clean などもすべて Maven プロジェクトがサポートしているプラグインとして機能実装されています.
プラグインは

  • ビルド時に実行するもの
  • サイト生成時に実行するもの

の2種類があります.5

Apache Maven のユースケース

Maven が目指す機能として以下が挙げられています.

  • ビルドプロセスの簡略化
  • 標準ビルドシステムの提供
  • プロジェクト情報の明示
  • 開発体験の向上

実際に Apache Maven を利用すると,

  • ひな形の Java プロジェクト作成
  • Java プロジェクトの依存関係管理
  • Java プロジェクトのビルド
  • ビルドアーティファクトのデプロイ
  • プロジェクトレポートの自動作成

が可能となり,集団あるいは個人の Java プロジェクト開発・公開を補助してくれます.

動作確認環境

動作確認を行った環境情報を以下に示します.

名称 バージョン
Windows 11 22H2
Ubuntu (WSL2) 20.04.6 LTS
OpenJDK 21
Maven 3.9.6

Java & Maven 環境準備手順

それぞれからバイナリを含む tar.gz を取得して展開,パスを通します.

mkdir -p $HOME/installers/linux/java && cd $_ && \
wget https://download.java.net/java/GA/jdk21/fd2272bbf8e04c3dbaee13770090416c/35/GPL/openjdk-21_linux-x64_bin.tar.gz &&
mkdir -p $HOME/extracts &&
tar -xvf openjdk-21_linux-x64_bin.tar.gz --directory $HOME/extracts
vim $HOME/.bashrc
> # Java
> export JAVA_HOME=$HOME/extracts/jdk-21
> export CLASSPATH=$JAVA_HOME/lib
> export PATH=$PATH:$JAVA_HOME/bin
source $HOME/.bashrc
## 確認
java -version

# Maven
## 取得~パスを通す
mkdir -p $HOME/installers/linux/maven && cd $_ && \
wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz &&
tar -xvf apache-maven-3.9.6-bin.tar.gz --directory $HOME/extracts
vim $HOME/.bashrc
> # Apache Maven
> export MVN_HOME=$HOME/extracts/apache-maven-3.9.6
> export PATH=$PATH:$MVN_HOME/bin
source $HOME/.bashrc
## 確認
mvn -v

Maven in 5 Minutes

公式の "Maven in 5 Minutes" に沿って動作確認をします.

# サンプルプロジェクト (アーキタイプ) の作成
mkdir -p $HOME/codes/GitHub && cd $_ &&
mvn archetype:generate
> ...
> Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 2099:
> Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
> 1: 1.0-alpha-1
> ...
> 8: 1.4
> Choose a number: 8:
> Define value for property 'groupId': com.caunus.sampleapp
> Define value for property 'artifactId': maven-training
> Define value for property 'version' 1.0-SNAPSHOT: :
> Define value for property 'package' com.caunus.sampleapp: :
> ...
> [INFO] ------------------------------------------------------------------------
> [INFO] BUILD SUCCESS
> [INFO] ------------------------------------------------------------------------

# pom.xml の確認,編集
cd maven-training && vim pom.xml

# アーティファクト (jar) 作成
mvn package

# アーティファクト (jar) 実行
java -cp ./target/maven-training-1.0-SNAPSHOT.jar com.caunus.sampleapp.App
> Hello World!

上記で pom.xml に記載してあるコンパイル目標 Java バージョンの指定を変更しています.

pom.xml
...
  <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>
    <!-- Java >9 -->
+    <maven.compiler.release>21</maven.compiler.release>
  </properties>
...

maven-archetype-archetype に基づいて作成されたプロジェクトの構成は以下,

maven-training
|-- pom.xml
|-- src
|   |-- main
|   |   `-- java
|   |       `-- com
|   |           `-- caunus
|   |               `-- sampleapp
|   |                   `-- App.java
|   `-- test
|       `-- java
|           `-- com
|               `-- caunus
|                   `-- sampleapp
|                       `-- AppTest.java
|
`-- target

標準のディレクトリレイアウトは以下です.

配置場所 配置物
/ .git, .svn
/ LICENSE.txt
/ NOTICE.txt
/ README.txt
/src/main/java アプリケーション/ライブラリのソース
/src/main/resources アプリケーション/ライブラリのリソース
/src/main/filters リソースフィルターファイル
/src/main/webapp Web アプリケーションのソース
/src/test/java テストソース
/src/test/resources テストリソース
/src/test/filters テストリソースフィルターファイル
/src/it 統合テスト (主にプラグイン用)
/src/assembly アセンブリ記述子
/src/site サイト

サンプル (アーキタイプ) はプラグインプロジェクト,Webアプリプロジェクトなどにも準備されています.6

App.java は以下のように実装されています.

App.java
package com.caunus.sampleapp;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

POM の読み方

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>com.caunus.sampleapp</groupId>
  <artifactId>maven-training</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>maven-training</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- <maven.compiler.source>1.8</maven.compiler.source> -->
    <!-- <maven.compiler.target>1.8</maven.compiler.target> -->
    <maven.compiler.release>21</maven.compiler.release>
  </properties>

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

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
タグ要素 用途 設定例
project 最上位要素 -
project >
modelVersion
POM が利用するオブジェクトモデルのバージョン 4.0.0 等
project >
groupId
プロジェクト作成組織の識別子
組織の完全修飾ドメイン名
プロジェクト作成時に指定
project >
artifactId
一意のアーティファクトベース名
-> アーティファクト (jar) ファイル名
プロジェクト作成時に指定
project >
version
アーティファクトのバージョン
-> アーティファクト (jar) ファイル名
プロジェクト作成時に指定
1.0-SNAPSHOT 等
project >
name
プロジェクトに使用される表示名
-> Git プロジェクト名
プロジェクト作成時に指定 (artifactId)
project >
url
プロジェクトのホストURL http://www.example.com
必要に応じて書き換える
project >
properties
POM 内横断で利用する設定値 <project.build.sourceEncoding>
<maven.compiler.source>
<maven.compiler.target>
project >
dependencies
依存関係を持つ"子"アーティファクト junit 等の外部依存関係
groupId/artifactId/version/scope を指定
project >
build
プラグイン,リソース管理 clean, compile
ビルドライフサイクルに用いるタスクを指定

SNAPSHOT バージョンとは,開発ブランチに沿った開発バージョンを示します.
リリース時には -SNAPSHOT が取れます.

命名規則

以下の規則に従って命名していきます.

groupId: 全てのプロジェクトに渡って一意であり,Java のパッケージ命名規則に従う

  • インターネットドメイン名を取得しておく
  • ドメイン名に含まれるコンポーネントを反転してプレフィックスとして使う
  • 以降は組織の規則に従う
    • 複数ジュールを扱うプロジェクトの場合は並列にする
  • ドメイン名にハイフンや特殊文字が含まれる場合はアンダースコア _ に変換する
  • 数字もしくは特殊文字で始まるコンポーネントは先頭にアンダースコア _ を付与する

artifactId: 小文字でわかりやすい名称,ハイフン利用可

version: 数字とドットを含む一般的なバージョン指定

ビルドライフサイクル

組み込みで実装されているビルドライフサイクルには以下の3つがあります.

  • Default
  • Clean
  • Site

それぞれが複数のフェーズで成り立っており,単一または複数の Apache 公式プラグインによって実装されています.

フェーズとは mvn コマンドで実行している cleanpackage 等で,ライフサイクルの中での段階を表します.
コマンドでフェーズが呼び出されると,Maven は呼び出されたフェーズまで順番に実行していきます.
例えば mvn package を実行すると,Default ビルドライフサイクルにて vaildate, compile, test フェーズを経由します.
ただし pre-*, post-*, process-* フェーズは通常コマンドで呼び出すことはせず,関連フェーズに紐づいて処理を実行します.

ゴールはプラグインの中で定義されている"処理単位"であり,フェーズと関連付けられて定義されています.
Maven では "プラグイン名:ゴール" の形式で mvn コマンドを叩くことで,ライフサイクルを無視してゴールだけを呼び出すこともできます.

# validate などのフェーズを無視して compile ゴールだけ実行する
mvn compiler:compile

ライフサイクル内のフェーズとデフォルトのプラグイン:ゴールとの紐づけが気になる場合は,以下をご参照ください.

Default ライフサイクル

フェーズ 用途
validate タスクに必要な情報, リソースが揃えられていることの検証
initialize ビルド状態を初期化
generate-sources コンパイルに含めるソースコードの生成
process-sources フィルタリングなどのソースコードの処理
generate-resources パッケージに含めるためのリソースの生成
process-resources リソースをコピーしてパッケージ化の準備
compile プロジェクトのソースコードのコンパイル
process-classes バイトコード拡張などのコンパイル生成ファイルの後処理
generate-test-sources コンパイルに含めるためのテストコードの生成
process-test-sources フィルタリングなどのテストコードの処理
generate-test-resources テスト用リソースの作成
process-test-resources リソースをコピーして処理
test-compile テストコードをテスト先にコンパイル
process-test-classes バイトコード拡張などのコンパイル生成ファイルの後処理
test 単体テストの実行
prepare-package パッケージ化前に必要なプロセスの実行
package コンパイルされたコードを配布可能なパッケージ (jar 等) 化
pre-integration-test 統合テスト前に必要なプロセスの実行
integration-test 統合テストを実行できる環境にデプロイ
post-integration-test 統合テスト後に必要なプロセスの実行
verify パッケージが有効で品質基準を満たしていることを検証
install ローカルリポジトリにインストール
deploy リモートリポジトリにコピー

Clean ライフサイクル

フェーズ 用途
pre-clean 削除前に必要なプロセスの実行
clean 以前のビルドで生成されたファイルの削除
post-clean 削除後に必要なプロセスの実行

Site ライフサイクル

フェーズ 用途
pre-site サイト生成前に必要なプロセスの実行
site サイトドキュメントの生成
post-site サイト生成完了 & デプロイに必要なプロセスの実行
site-deploy サイトドキュメントを指定の Web サーバーにデプロイ

POM の継承・集約

POM ファイルは継承や集約をすることができます.
ここで,継承は単一の親に対してのみ行うことができます.
複数の親から継承を行いたい場合は,Dependency scope の import を利用します.

いま my-app を my-modlue の親とすることを想定します.

my-app 用の pom.xml (親)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>
my-module 用の pom.xml (子)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

ケース 1

親が子から観測可能である場合,

my-app
|-- pom.xml
`-- my-module
    `-- pom.xml

my-module 用の pom.xml (子) に parent タグ要素を入れることで,my-module (子) から my-app (親) を参照 (継承) できます.

my-module 用の pom.xml (子)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
  </parent>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

また,my-app 用の pom.xml (親) に packaging タグ要素を入れることで,my-app (親) に my-module (子) を集約することもできます.

my-app 用の pom.xml (親)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>my-module</module>
  </modules>
</project>

ケース 2

親と子が切り離されている場合,

.
|-- my-app
|   `-- pom.xml
`-- my-module
    `-- pom.xml

relativePath タグ要素を使って相対パスを使うことで継承,

my-module 用の pom.xml (子)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>../my-app/pom.xml</relativePath>
  </parent>
 
  <artifactId>my-module</artifactId>
</project>

あるいは集約を行うことができます.

my-app 用の pom.xml (親)
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>../my-module</module>
  </modules>
</project>

プロファイルの設定

ローカルの状況に依存する設定や調整が必要な場合 (例えばプロキシ設定等),プロファイルを与えて Maven を設定できます.
状況に応じて以下の場所に設定を記載します.

ケース プロファイル場所
ユーザーごとに設定 $HOME/.m2/settings.xml
グローバルに設定 $HOME/extracts/apache-maven-3.9.6/conf/settings.xml
(参考) プロジェクトの設定 pom.xml

プロファイルを埋め込むことで移植性が損なわれたり,POM 継承などを行っている環境でどの設定が有効化されているか確認しなければならないなどの面倒が付きまとう可能性もあります.

詳しくは,公式のプロファイル概要プロファイル設定項目をご確認ください.

リポジトリ

image.png

リポジトリ (アーティファクトの格納場所) は以下の2つを利用します.

  • リモートリポジトリ: インターネット上あるいは組織で公開するアーティファクトを格納
  • ローカルリポジトリ: リモートリポジトリから取得したアーティファクト,リリースしていないアーティファクトを格納 (mvn install 等)

ローカルリポジトリは,ディスクの容量問題などがない限りは特に作業を行うことはありません.
ローカルリポジトリはデフォルトで $HOME/.m2/repository/ を使います.
特殊な事情でローカルリポジトリの場所を変更したい場合は,以下の設定を適切なプロファイルに記載します.

settings.xml
<settings>
  ...
  <localRepository>/path/to/local/repo/</localRepository>
  ...
</settings>

ローカルリポジトリに存在しないアーティファクトは,リモートリポジトリへ探しに行きます.
デフォルトでは maven central repository に問い合わせを行います.

他のリポジトリも利用したい場合は,適切なプロファイルに設定を記載します.
Sonatype Nexus OSS, Gitea OSS, Reposilite OSS 等のリポジトリマネージャーを独自でホストしている場合もこちらを使います.

pom.xml
<project>
...
  <repositories>
    <repository>
      <id>my-repo1</id>
      <name>your custom repo</name>
      <url>http://jarsm2.dyndns.dk</url>
    </repository>
    <repository>
      <id>my-repo2</id>
      <name>your custom repo</name>
      <url>http://jarsm2.dyndns.dk</url>
    </repository>
  </repositories>
...
</project>
settings.xml
<settings>
 ...
 <profiles>
   ...
   <profile>
     <id>myprofile</id>
     <repositories>
       <repository>
         <id>my-repo2</id>
         <name>your custom repo</name>
         <url>http://jarsm2.dyndns.dk</url>
       </repository>
     </repositories>
   </profile>
   ...
 </profiles>
 
 <activeProfiles>
   <activeProfile>myprofile</activeProfile>
 </activeProfiles>
 ...
</settings>

なお pom.xmlsettings.xml どちらも設定した場合は,settings.xml が優先されます.

maven central repository に関しても,地理的により近いミラーリポジトリに差し替えることができます.

settings.xml
<settings>
  ...
  <mirrors>
    <mirror>
      <id>other-mirror</id>
      <name>Other Mirror Repository</name>
      <url>https://other-mirror.repo.other-company.com/maven2</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

ちなみにですが,maven central repository 全体の独自ミラーリポジトリを作成することは禁止されています.

インターネットに公開済みのアーティファクトの検索には mvn repository がしばしば利用されています.こちらではアーティファクトを検索して直接ダウンロードすることもできます.

なお,オフラインでビルドを行いたい場合は -o オプションを使うことができます.

mvn -o package

依存関係の管理

推移的依存関係

アーティファクトの依存関係をさらに読み取る構成を取ることで,Maven はマルチモジュールなアプリケーションでも毎回のすべての依存関係を記述することがなく利用できます.
また,爆発的に依存関係が大規模化してしまうことを防ぐ方法も準備されています.

  • Dependency mediation

同じものを複数回参照した場合,ツリーで浅いものを優先して利用する "nearest definition"

D 1.0 が採用される
  A
  ├── B
  │   └── C
  │       └── D 2.0
  └── E
      └── D 1.0
D 2.0 を明示すれば D 2.0 が強制的に採用される
  A
  ├── B
  │   └── C
  │       └── D 2.0
  ├── E
  │   └── D 1.0
  │
  └── D 2.0  

プロジェクトがどのような依存関係を持っているかをツリーで確認する方法として mvn dependency:tree も準備されています.

mvn dependency:tree
> ...
> [INFO] com.caunus.sampleapp:maven-training:jar:1.0-SNAPSHOT
> [INFO] \- junit:junit:jar:4.11:test
> [INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
> [INFO] ------------------------------------------------------------------------
> [INFO] BUILD SUCCESS
> ...
  • Dependency scope

スコープの設定によって,推移的依存関係が解決されてクラスパスに含められるタイミングを指定することができます.
ただし,import スコープは POM の継承に利用します.

スコープ 用途
compile デフォルト
provided JDK または コンテナ実行時にのみ含めたいものに指定
i.e. コンパイル時およびテスト時には含めるが,実行時には含めない
runtime コンパイルには不要だが実行には含めたいものに指定
i.e. テスト時および実行時には含めるが,コンパイル時には含めない
test アプリケーションには依存関係がなく,テストにのみ含めたいものに指定
i.e. テスト時には含めるが,コンパイル時および実行時には含めない
system provided と含めるタイミングは同じだが,明示的に含めてありリモートレポジトリでは検索しない場合に指定
import <type>pom</type> に対してのみ指定できる
複数の POM から継承を行う
  • Optional dependencies

X -> A -> B 関係にあるとき,X で B の機能が使用されない場合は A から B の依存関係は張らなくてもよいということを,optional タグ要素を使うことで明示的に設定することができます.

pom.xml
<project>
  ...
  <dependencies>
    <!-- declare the dependency to be set as optional -->
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <optional>true</optional> <!-- value will be true or false only -->
    </dependency>
  </dependencies>
</project>
  • Excluded dependencies

X -> A -> B 関係にあるとき,X は B を意図的に依存関係から除外するということを,exclusions タグ要素を使うことで明示的に設定することができます.

pom.xml
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>  <!-- declare the exclusion here -->
          <groupId>sample.ProjectB</groupId>
          <artifactId>Project-B</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
  </dependencies>
</project>

Banned dependencies を使えば,モジュールのブラックリストを作成し,合致したものが見つかってしまった場合はビルドが失敗するように設定することもできます.

プラグイン作成

Maven プラグインは Maven plain Old Java Object (Mojo) (※ 少し前話題になっていた Python alternative の Mojo とは別物です!) を利用して作成することができます.

命名規則

<yourplugin>-maven-plugin とします.
maven-<yourplugin>-plugin は Apache Maven 公式プラグインで利用されているため NG です.

作成 & 組み込み手順

プラグイン用のアーキタイプを利用してプロジェクトを作成します.
本記事ではアーキタイプのサンプルのままでビルドを行います.
このサンプルプラグインは touch ゴールを持ち,touch.txt を target に生成します.

cd $HOME/codes/GitHub &&
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-plugin -DarchetypeVersion=1.4
> ...
Define value for property 'groupId': com.caunus.sampleplugin
Define value for property 'artifactId': touch-maven-plugin
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' com.caunus.sampleplugin: :
> ...
> [INFO] ------------------------------------------------------------------------
> [INFO] BUILD SUCCESS
> [INFO] ------------------------------------------------------------------------

# pom.xml の確認,編集
cd touch-maven-plugin && vim pom.xml

# アーティファクト (jar) 作成 & インストール
mvn clean install
> ...
> Installing /.../codes/GitHub/touch-maven-plugin/target/touch-maven-plugin-1.0-SNAPSHOT.jar to /.../.m2/repository/com/caunus/sampleplugin/touch-maven-plugin/1.0-SNAPSHOT/touch-maven-plugin-1.0-SNAPSHOT.jar
> ...

# maven-training に戻って依存関係に加える
cd $HOME/codes/GitHub/maven-training && vim pom.xml

# プラグインで実装した機能を実行
# ゴールだけを実行する (pom.xml に書いておかなくても動く)
mvn com.caunus.sampleplugin:touch-maven-plugin:1.0-SNAPSHOT:touch

# mvn package で touch ゴールも動くことを確認する
mvn package
> ...
> [INFO] --- jar:3.0.2:jar (default-jar) @ maven-training ---
> [INFO] Building jar: /.../codes/GitHub/maven-training/target/maven-training-1.0-SNAPSHOT.jar
> [INFO]
> [INFO] --- touch:1.0-SNAPSHOT:touch (default) @ maven-training ---
> [INFO] ------------------------------------------------------------------------
> [INFO] BUILD SUCCESS
> ...

上記で touch-maven-plugin 用の pom.xml に記載してあるコンパイル目標 Java バージョンの指定,maven-plugin-plugin のバージョンを変更しています.

プラグイン用の pom.xml
...
  <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>
    <!-- Java >9 -->
+    <maven.compiler.release>21</maven.compiler.release>
  </properties>
...
        <plugin>
          <artifactId>maven-plugin-plugin</artifactId>
-          <version>3.6.0</version>
+          <version>3.11.0</version>
        </plugin>
...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-plugin-plugin</artifactId>
-        <version>3.6.0</version>
+        <version>3.11.0</version>
        <configuration>
          <!-- <goalPrefix>maven-archetype-plugin</goalPrefix> -->
          <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
        </configuration>
...

また,maven-training 用の pom.xml には touch-maven-plugin が package フェーズに稼働するように設定しています.

maven-training の pom.xml
...
  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        ...
+        <!-- custom plugin -->
+        <plugin>
+          <groupId>com.caunus.sampleplugin</groupId>
+          <artifactId>touch-maven-plugin</artifactId>
+          <version>1.0-SNAPSHOT</version>
+        </plugin>
      </plugins>
    </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>com.caunus.sampleplugin</groupId>
+        <artifactId>touch-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>touch</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
  </build>
...

プラグインのゴールを定義している MyMojo.java は以下の様に実装されています.

MyMojo.java
package com.caunus.sampleplugin;


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;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Goal which touches a timestamp file.
 */
@Mojo( name = "touch", defaultPhase = LifecyclePhase.PROCESS_SOURCES )
public class MyMojo
    extends AbstractMojo
{
    /**
     * Location of the file.
     */
    @Parameter( defaultValue = "${project.build.directory}", property = "outputDir", required = true )
    private File outputDirectory;

    public void execute()
        throws MojoExecutionException
    {
        File f = outputDirectory;

        if ( !f.exists() )
        {
            f.mkdirs();
        }

        File touch = new File( f, "touch.txt" );

        FileWriter w = null;
        try
        {
            w = new FileWriter( touch );

            w.write( "touch.txt" );
        }
        catch ( IOException e )
        {
            throw new MojoExecutionException( "Error creating file " + touch, e );
        }
        finally
        {
            if ( w != null )
            {
                try
                {
                    w.close();
                }
                catch ( IOException e )
                {
                    // ignore
                }
            }
        }
    }
}

サイトの生成

Maven はプロジェクトレポート (サイト) を簡単に生成することができます.

mvn site

上記を実行するとプロジェクト target 配下に html, css 等が生成されます.

maven-training
|-- pom.xml
|-- src
`-- target
    |-- site
    |   |-- css
    |   |-- images
    |   |-- surefire-reports
    |   |-- test-classes
    |   |-- dependencies.html
    |   |-- dependency-info.html
    |   |-- index.html
    |   |-- plugin-management.html
    |   |-- plugins.html
    |   |-- project-info.html
    |   `-- summary.html
    `-- ...

dependencies.html
image.png

dependency-info.html
image.png

一式で取得できるので,様々な方法を使って Web サーバーにデプロイすることができます.
GitHub Pages を利用したい場合,公式ドキュメントでは Maven SCM Publish Plugin を使ったデプロイ方法が紹介されています7が,GitHub Pages の設定に合わせて一式を丸々移すだけでも簡単にデプロイできます.

# GitHub で maven-training リポジトリを作成後
# まずは普通に push
echo "# maven-training" >> README.md
echo "/target/*" >> .gitignore
git init && git add . && git commit -m "init"
git branch -m master main
git remote add origin https://github.com/caunu-s/maven-training.git
git push -u origin main

# orphan ブランチを作成して Pages 専用にする
git checkout --orphan gh-pages
mv target/site/* .
rm pom.xml README.md && rm -rf src && rm -rf target
git add . && git commit -m "init site" && git push origin gh-pages

# サイト更新,main ブランチで mvn package 後を想定
git checkout gh-pages
rm *.html && rm -rf css && rm -rf images
mv target/site/* . && rm -rf target
git add . && git commit -m "update site" && git push origin gh-pages

最後に

今日では Gradle の選択肢があったり,Java 以外が好まれて使われる状況であったりと Maven を改めて学ぶモチベーションは湧きにくいかもしれません.

一応,Google Trends に聞いてみて「まだ Maven も関心は持たれてるのかなぁ」と思ったりはします.

image.png

入門レベルでいい具合に記事をまとめられた気はするので「Maven はこれだけやっとけばいいや」ぐらいな感じで読んでもらえていたら嬉しいです.

  1. https://maven.apache.org/ref/3.9.6/

  2. https://maven.apache.org/community.html

  3. https://maven.apache.org/guides/development/guide-helping.html

  4. https://maven.apache.org/index.html

  5. https://maven.apache.org/plugins/index.html

  6. https://maven.apache.org/archetypes/index.html

  7. https://maven.apache.org/guides/mini/guide-site.html#github-pages-apache-svnpubsub-gitpubsub-deployment

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3