Mavenの基本勉強メモ

ずっと Gradle 使ってたけど、お仕事で Maven をゴリゴリに使わないといけなくなったのでお勉強。

# Maven とは
Java のビルドツール。OSS。
Apache Ant に代わるものとして作られたらしい。

読み方は「メイヴン」または「メイヴェン」（自分はメイヴン派）。

結構昔からあるけど[^1]、現在も開発が続けられているし、 Maven を採用しているプロジェクトも多い印象。
2020年現在、Java のビルドツールといえば Maven か Gradle が選ばれることが多い（と思う）。

[^1]: Maven 1.0 が 2004 年、Maven 2.0 が 2005 年、Maven 3.0 が 2010 年（[Maven – Maven Releases History](http://maven.apache.org/docs/history.html)）

2020年現在のメジャーバージョンは3。
Maven 1 と 2 は互換性が無いが、2 と 3 は互換性が保たれている。

# 環境
```
>mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: ...\bin\..
Java version: 11.0.6, vendor: AdoptOpenJDK, runtime: ...
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
```

# インストール
※JDK のインストール方法は割愛。

- [公式サイト](http://maven.apache.org/download.cgi#)からアーカイブ(`apache-maven-X.X.X-bin.zip`)をダウンロードしてくる
- 適当なところで解凍する

```text:解凍後の様子
`-apache-maven-X.X.X/
  |-bin/
  |-boot/
  |-conf/
  |-lib/
  |-LICENSE
  |-NOTICE
  `-README.txt
```

※解凍先のフォルダ（上の例でいうと `apache-maven-X.X.X`）を、以後は `%MAVEN_HOME%` と記述する。

- `%MAVEN_HOME%\bin` にパスを通す
- 以下のコマンドを実行してバージョンの情報が出たらインストール完了

```text:インストールの確認
>mvn --version
Apache Maven X.X.X (...)
...
```

# 設定ファイル
プロジェクトをまたがって共通な設定は、以下のいずれかのファイルに記述することができる。

- `%MAVEN_HOME%\conf\settings.xml`
- `%USERPROFILE%\.m2\settings.xml` [^2]

[^2]: `%USERPROFILE%` はユーザのホームを指す Windows の環境変数（Linux とかなら `$HOME` のこと）

前者のファイルに設定した内容は、ユーザをまたがってすべてのプロジェクトで共通となる。
後者のファイルに設定した内容は、そのユーザ内でプロジェクトをまたがって共通となる。

前者のファイルはダウンロードした zip の中に入っているが、全ての設定は空になっていてコメントで説明が書かれている。
後者のファイルは最初は存在しないので、前者のファイルをコピーして作ると良い。

## プロキシの設定
```xml:settings.xml
<settings ...>
  ...
  <proxies>
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
  </proxies>
  ...
</settings>
```

必要な設定を書き換えて、前述のいずれかの `settings.xml` に記述する。

# Hello World
## プロジェクトを生成する
- 適当なフォルダでコマンドラインを開き、以下のコマンドを実行する。

```text
> mvn archetype:generate
```

- 初回は色々ダウンロードなどの処理が走った後に、次のような一覧が表示される。

```
...
Choose archetype:
1: internal -> org.appfuse.archetypes:appfuse-basic-jsf (AppFuse archetype for creating a web application with Hibernate, Spring and JSF)
2: internal -> org.appfuse.archetypes:appfuse-basic-spring (AppFuse archetype for creating a web application with Hibernate, Spring and Spring MVC)
3: internal -> org.appfuse.archetypes:appfuse-basic-struts (AppFuse archetype for creating a web application with Hibernate, Spring and Struts 2)
...
16: internal -> org.apache.maven.archetypes:maven-archetype-quickstart ()
...
57: internal -> org.fusesource.scalate.tooling:scalate-archetype-empty (Generates a Scalate empty web application)
58: internal -> org.fusesource.scalate.tooling:scalate-archetype-guice (Generates a Scalate Jog web application)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 16:
```

- `Choose archetype` と表示され、使用するアーキタイプの番号を入力するように促される
- とりあえずデフォルトとなっている 16 (`org.apache.maven.archetypes:maven-archetype-quickstart`) を選択してみる（そのままEnter を入力すれば、デフォルトの番号が選択される）

```
Define value for property 'groupId': : 【com.example】
Define value for property 'artifactId': : 【hello-world】
Define value for property 'version':  1.0-SNAPSHOT: :【】
Define value for property 'package':  com.example: :【】
Confirm properties configuration:
groupId: com.example
artifactId: hello-world
version: 1.0-SNAPSHOT
package: com.example
 Y: : 【y】
```

- 作成するプロジェクトの基本情報の入力が促されるので、適当に入力していく
    - 上記例は、`【】`で囲った部分がキーボードで入力した内容となる（空の `【】` は何も入力せずに Enter した箇所）
    - それぞれの意味はおいおい説明
- 最後に入力した情報で作成してよいか聞かれるので、問題なければ `y` を入力して Enter
- `BUILD SUCCESS` と表示されれば成功

## 生成されたプロジェクトの確認
- カレントフォルダに `artifactId` で指定した名前と同じ名前のフォルダが作られている
    - 上の例で作った場合は `hello-world` というフォルダが作られている
- `hello-world` フォルダの中身は次のようになっている

```text:hello-worldフォルダの中身
hello-world/
|-pom.xml
`-src/
  |-main/java/
  | `-com/example/
  |    `-App.java
  `-test/java/
    `-com/example/
      `-AppTest.java
```

```xml: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.example</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>

  <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>
  </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>
        ...
      </plugins>
    </pluginManagement>
  </build>
</project>
```

```java:App.java
package com.example;

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

- 簡単な Hello World プログラムのプロジェクトが生成されている

## コンパイルして実行する
- 以下のコマンドを実行してソースコードをコンパイルする

```text:コンパイル
> mvn compile
```

- これまた、初回は色々ダウンロード処理が走る（２回目以降はダウンロードがなくなるので速くなるはず）
- `BUILD SUCCESS` と表示されたら成功
- `hello-world/target/classes/` の下に、 `App.java` のコンパイル結果が出力されている
- 以下のコマンドで実行する

```text:hello-worldを実行する
> java -cp target\classes com.example.App
Hello World!
```

## 説明
### アーキタイプ
- 最初の `mvn archetype:generate` は、**アーキタイプ**と呼ばれるプロジェクトをひな形から自動生成する仕組みを実行している
    - ここでは、最もシンプルな `maven-archetype-quickstart` を選択してプロジェクトを自動生成している

# POM
[Maven – Introduction to the POM](http://maven.apache.org/guides/introduction/introduction-to-the-pom.html)

Maven の設定は pom.xml という XML ファイルで記述する。
POM は、 Project Object Model の頭文字を取った略となっている。

Maven はビルド対象を**プロジェクト**という単位で管理する。
POM は、そのプロジェクトについての様々な設定を記述するためのファイルとなる。

## Super POM
POM には親子関係があり、全ての POM の最上位の親として**Super POM**というものが存在する。
例えば、最新の Super POM は次のページで確認できる。

[Maven Model Builder – Super POM](https://maven.apache.org/ref/current/maven-model-builder/super-pom.html)

プロジェクトの POM に設定がない場合は、基本的に親 POM の設定が継承されるようになっている。
つまり、 Super POM はすべてのプロジェクトで適用されるデフォルトの設定が記述された POM ということになる。

## 最小構成の POM
最小構成の POM を作るとすると、 pom.xml の内容は次のようになる。

```xml:最小構成の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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

- 一番上に `<project>` タグを記述する
- その中には、少なくとも次の４つのタグを記述しなければならない
    - `<modelVersion>`
    - `<groupId>`
    - `<artifactId>`
    - `<version>`
- `<modelVersion>` は、 POM のバージョンを指定している
    - これは基本的に変わることが無いので、とりあえずおまじない的にこの値を指定しておけばいいと思う
- `<groupId>`, `<artifactId>`, `<version>` の３つは、プロジェクトを一意に識別するための情報を定義している
    - プロジェクトの完全修飾名は、 `<groupId>:<artifactId>:<version>` となる
    - つまり、上記設定の場合は `example:hello:1.0.0` がこのプロジェクトの完全修飾名となる
    - `<groupId>`
        - `groupId` には、そのプロジェクトが属する組織や上位プロジェクトを識別できる名前を指定する
        - 例えば、 Maven が提供する多くのプラグインは `org.apache.maven.plugins` という `groupId` が指定されている
        - `.` は付いてもついていなくてもいい（`junit` などは付いていない）
        - `groupId` の値と、そのプロジェクトの Java パッケージ構成が一致している必要はないが、一致させておいたほうが混乱が少なくて無難
    - `<artifactId>`
        - そのプロジェクトを識別する名前を指定する
        - 例えば、 Maven が提供しているコンパイラプラグインは `maven-compiler-plugin` という `artifactId` となっている
    - `<version>`
        - `version` は、そのプロジェクトのバージョンを指定する
        - `groupId` と `artifactId` によってプロジェクトが一意に特定され、さらに `version` によってバージョンが特定されることになる
- この pom.xml に記述されていない設定は、基本的に [Super POM](https://maven.apache.org/ref/current/maven-model-builder/super-pom.html) から継承される[^3]
    - つまり、記述されていない `<repository>` の設定は、 `<url>https://repo.maven.apache.org/maven2</url>` が採用されていることになる
    - ただ、実は Super POM に記述されてないけどデフォルトで採用されている設定も存在している（`<packaging>` とか）
        - そのへんの話はおいおい

[^3]: 「`<modelVersion>` も Super POM にあるやん」って思うけど、これは残念ながら継承されないもよう（消すとエラーになる）

### 開発バージョンを表すプレフィックス
- アーティファクトのバージョン番号について、決まった採番のルールはない（たぶん）
- ただし、末尾に `-SNAPSHOT` とついたバージョンについては、特別な意味がある
- `-SNAPSHOT` プレフィックスが付いたバージョンは、それが開発中のバージョンであることを表している
- 開発中のバージョンは、アーティファクト（jar）の中身が更新される可能性があることを表している
- 一方で、 SNAPSHOT が付いていないバージョンは基本的にリリースバージョンを表しており、中身が更新されない（というのがお約束）
- あくまでお約束なので、仕組みで縛られているわけではない
    - 後述するリポジトリ（Nexus）の機能で、リリースバージョンは更新不可、SNAPSHOTは更新可にするような制御は可能

## 変数
pom.xml の中では、同じ値を複数の箇所で重複して記述してしまう問題を回避するため、変数を参照できるようになっている。

### プロジェクトモデル変数
```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>project.groupId = ${project.groupId}</echo>
            <echo>project.artifactId = ${project.artifactId}</echo>
            <echo>project.version = ${project.version}</echo>
            <echo>project.build.directory = ${project.build.directory}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- 何やらいきなりいっぱい現れたが、ここで重要なのは次の箇所になる

```xml:重要な部分だけ抽出
            <echo>project.groupId = ${project.groupId}</echo>
            <echo>project.artifactId = ${project.artifactId}</echo>
            <echo>project.version = ${project.version}</echo>
            <echo>project.build.directory = ${project.build.directory}</echo>
```

- いくつかの変数の値を出力(echo)している
    - この周りにいっぱい書いてあるやつは、 echo ができるようにするためにプラグインを追加している
- これを実行すると、次のようになる

```text:実行結果
> mvn antrun:run
...
     [echo] project.groupId = example
     [echo] project.artifactId = hello
     [echo] project.version = 1.0.0
     [echo] project.build.directory = F:\tmp\maven\hello\target
...
```

- `${変数の参照式}` で記述した部分が、式の評価結果に置き換えられて出力されているのが分かる
- この例で参照している変数は、全て**プロジェクトモデル変数(Project Model Variable)**と呼ばれるものになる
- 要するに、 pom.xml 内の `<project>` 配下のタグの値を参照している
    - `project.version` は、 `<project><version>` の値を参照している
    - `project.build.directory` は、この pom.xml の中にはないが [Super POM](https://maven.apache.org/ref/current/maven-model-builder/super-pom.html) の中で宣言されている

### 式の構文
```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>project.class = ${project.class}</echo>
            <echo>project.getClass() = ${project.getClass()}</echo>
            <echo>plugins[0].artifactId = ${project.build.plugins[0].artifactId}</echo>
            <echo>plugins[1].artifactId = ${project.build.plugins[1].artifactId}</echo>
            <echo>plugins[2].artifactId = ${project.build.plugins[2].artifactId}</echo>
          </target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jdeps-plugin</artifactId>
        <version>3.1.2</version>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn antrun:run
...
     [echo] project.class = class org.apache.maven.model.Model
     [echo] project.getClass() = ${project.getClass()}
     [echo] plugins[0].artifactId = maven-antrun-plugin
     [echo] plugins[1].artifactId = maven-jdeps-plugin
     [echo] plugins[2].artifactId = ${project.build.plugins[2].artifactId}
```

- `${...}` の中に記述する式は、基本的にドット (`.`) 区切りでオブジェクトのプロパティを参照していけるようになっている
    - `foo` という名前のプロパティを参照すると、裏では `getFoo()` （または `isFoo()`）というメソッドが実行されている
    - つまり、 Getter さえあれば対応するプロパティ名で参照できる（`class` も参照できる）
- ただし、参照できるのはプロパティのみで、メソッドを直接実行したりはできない
- プロパティが配列または `List` の場合は、角括弧 (`[]`) を使ってインデックス参照ができる
- 式が正常に評価できない場合は、ただの文字列として処理される
- `project` 変数の実体は、 [org.apache.maven.model.Model](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/Model.html) というクラスのインスタンスとなっている
    - この `Model` インスタンスが [ReflectionValueExtractor](https://github.com/apache/maven-shared-utils/blob/master/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java#L163) に渡されることによって式が評価されている

#### Mapの参照
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>${project.build.pluginsAsMap(org.apache.maven.plugins:maven-antrun-plugin).id}</echo>
            <echo>${project.build.pluginsAsMap(org.apache.maven.plugins:maven-jdeps-plugin).id}</echo>
          </target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jdeps-plugin</artifactId>
        <version>3.1.2</version>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn antrun:run
...
     [echo] org.apache.maven.plugins:maven-antrun-plugin:1.8
     [echo] org.apache.maven.plugins:maven-jdeps-plugin:3.1.2
```

- プロパティが `Map` の場合、 `fooMap(<key>)` という形でキー指定の参照ができる
    - `<key>` はダブルクォーテーション (`"`) などで括る必要はなく、そのまま `String` のキーとして使用される
- `pluginsAsMap` というプロパティは、 [PluginContainer](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/PluginContainer.html#getPluginsAsMap--) というクラスに定義されたメソッドを参照している
    - この `Map` は、キーに [Plugin#getKey()](https://maven.apache.org/ref/3.6.3/maven-model/apidocs/org/apache/maven/model/Plugin.html#getKey--) の結果、値に該当する `Plugin` インスタンスが設定されている
        - `Plugin#getKey()` は、そのプラグインの `groupId` と `artifactId` をコロン (`:`) でつなげた値を返す
    - `project.build` で参照できる [Build](https://maven.apache.org/ref/3.6.3/maven-model/apidocs/org/apache/maven/model/Build.html) クラスは、この `PluginContainer` を継承している
- このように、一部のクラスには `List` を `Map` 形式に変換したプロパティを提供しているものがある
    - 具体的には、次のクラスが `～AsMap` を提供している
    - [PluginContainer#getPluginsAsMap()](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/PluginContainer.html#getPluginsAsMap--)
    - [Plugin#getExecutionsAsMap()](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/Plugin.html#getExecutionsAsMap--)
    - [Reporting#getReportPluginsAsMap()](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/Reporting.html#getReportPluginsAsMap--)
    - [ReportPlugin#getReportSetsAsMap()](https://maven.apache.org/ref/current/maven-model/apidocs/org/apache/maven/model/ReportPlugin.html#getReportSetsAsMap--)

#### 各クラスのプロパティ
`Model` から参照できる各クラスやプロパティの全体像をクラス図でまとめた。

![maven.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/d7147459-768d-393e-f7e2-64dc85eb9aab.jpeg)

- <font color="red">赤線が継承</font>
- <font color="blue">青線が単一の参照</font>
- <font color="green">緑線が List での参照</font>


### 特殊変数
- プロジェクトモデルには含まれていないが、特別に定義されていて参照が可能な変数がいくつか存在する

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  
  <build>
    ...
          <target>
            <echo>project.basedir = ${project.basedir}</echo>
            <echo>project.baseUri = ${project.baseUri}</echo>
            <echo>maven.build.timestamp = ${maven.build.timestamp}</echo>
          </target>
    ...
  </build>
</project>
```

```text:実行結果
     [echo] project.basedir = F:\tmp\maven\hello
     [echo] project.baseUri = file:///F:/tmp/maven/hello/
     [echo] maven.build.timestamp = 2020-03-04T13:45:10Z
```

- 次の３つは、**特殊変数(Special Variables)**として暗黙的に定義されている変数となる
    - `project.basedir`
        - プロジェクト自体のフォルダ
    - `project.baseUri`
        - `project.basedir` を URI で表したもの
    - `maven.build.timestamp`
        - 実行時のタイムスタンプ(UTC)

#### タイムスタンプのフォーマットを指定する
- `maven.build.timestamp.format` というプロパティを宣言することで、 `maven.build.timestamp` のフォーマットを任意に指定することができる
- なお、フォーマットの書式は [SimpleDateFormat](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/text/SimpleDateFormat.html) に従う

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <properties>
    <maven.build.timestamp.format>yyyy/MM/dd HH:mm:ss</maven.build.timestamp.format>
  </properties>
  
  <build>
    ...
            <echo>maven.build.timestamp = ${maven.build.timestamp}</echo>
    ...
  </build>
</project>
```

```text:実行結果
     [echo] maven.build.timestamp = 2020/03/04 13:49:49+00:00
```

### プロパティ
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <properties>
    <foo>FOO!!</foo>
    <fizz.buzz>FIZZ.BUZZ!?</fizz.buzz>
    <hoge-fuga>HOGE-FUGA??</hoge-fuga>
  </properties>
  
  <build>
    ...
            <echo>foo = ${foo}</echo>
            <echo>fizz.buzz = ${fizz.buzz}</echo>
            <echo>hoge-fuga = ${hoge-fuga}</echo>
            <echo>FOO = ${FOO}</echo>
    ...
  </build>
</project>
```

```text:実行結果
     [echo] foo = FOO!!
     [echo] fizz.buzz = FIZZ.BUZZ!?
     [echo] hoge-fuga = HOGE-FUGA??
     [echo] FOO = ${FOO}
```

- `<properties>` タグの中で独自の変数（プロパティ）を宣言できる
- `-` や `.` を名前に含めることが可能
- 大文字・小文字は区別される

### 環境変数
```xml.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  
  <build>
    ...
            <echo>hoge = ${env.hoge}</echo>
            <echo>Hoge = ${env.Hoge}</echo>
            <echo>HOGE = ${env.HOGE}</echo>
    ...
  </build>
</project>
```

```text:実行結果(Windows上で動かした場合)
> set Hoge=Hello

> mvn antrun:run
...
     [echo] hoge = ${env.hoge}
     [echo] Hoge = ${env.Hoge}
     [echo] HOGE = Hello
...
```

```text:実行結果(Linux上で動かした場合)
$ export Hoge=Hello

$ mvn antrun:run
...
     [echo] hoge = ${env.hoge}
     [echo] Hoge = Hello
     [echo] HOGE = ${env.HOGE}
```

- `${env.環境変数名}` で、 OS の環境変数の値を参照できる
- Windows 上で動く場合、 `env` の変数名は**すべて大文字で正規化されている**
    - つまり、 Windows 上で宣言されている環境変数の名前がたとえ `Path` であっても、 pom.xml 上で参照するときは `${env.PATH}` と大文字で記述しなければならない
- あくまで Windows 上での話で、 Linux 上で動かす場合は大文字・小文字を区別したそのままの名前で指定する
- まぁ、環境変数は全部大文字で宣言するのが一般的だと思うので、 pom.xml も全部大文字で書いておけば事故ることはないと思う

### システムプロパティ
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  
  <properties>
    <foo.bar>foo-bar</foo.bar>
    <fizz.buzz>fizz-buzz</fizz.buzz>
  </properties>

  <build>
    ...
            <echo>java.version = ${java.version}</echo>
            <echo>java.vm.vendor = ${java.vm.vendor}</echo>
            <echo>foo.bar = ${foo.bar}</echo>
            <echo>fizz.buzz = ${fizz.buzz}</echo>
    ...
  </build>
</project>
```

```text:実行結果
> mvn antrun:run -Dfoo.bar=FOO-BAR
...
     [echo] java.version = 11.0.6
     [echo] java.vm.vendor = AdoptOpenJDK
     [echo] foo.bar = FOO-BAR
     [echo] fizz.buzz = fizz-buzz
```

- Java のシステムプロパティ（`System.getProperties()` で取得できる値）を、そのまま `${システムプロパティ名}` で参照できる
- `<properties>` で同じ名前のプロパティが宣言されている場合は、システムプロパティで指定した値が優先される

# リポジトリ
- Maven の大事な構成要素の１つに**リポジトリ(Repository)**がある
- Maven では、作成したプロジェクトの成果物（アーティファクト）をリポジトリに保存して管理する
    - アーティファクトの実体は、普通はそのプロジェクトをビルドしてできた jar ファイル

## セントラルリポジトリ
- リポジトリには２種類ある
    - ローカルリポジトリ
    - リモートリポジトリ
- デフォルトで使用されるリモートリポジトリとして、[セントラルリポジトリ](https://repo.maven.apache.org/maven2)というものが存在する
    - ↑のリンクをブラウザで開けば、様々なサブディレクトリが存在することが分かる
    - ディレクトリを適当に辿っていくと、最終的に jar ファイルが配置されたディレクトリにたどり着く
    - 例えば[これ](https://repo.maven.apache.org/maven2/org/springframework/spring-core/5.2.4.RELEASE/)は、Spring Framework の core の ver 5.2.4.RELEASE が配置されたディレクトリになる
- このように、セントラルリポジトリには世界中の様々な OSS のアーティファクトが収められている
- セントラルリポジトリの管理は [Sonatype](https://www.sonatype.com/) という会社が行っている
    - 申請を行えば、誰でもセントラルリポジトリで自分の OSS を公開できる（無料）
    - ただし、申請は英語
    - 申請の単位は `groupId` ごと
    - 「Maven セントラルリポジトリ 公開 手順」とかで検索すれば、いろいろ解説記事が出てくる
- セントラルリポジトリを直接ブラウザで開いたものは検索がしづらいので、普通は [Maven Repository: Search/Browse/Explore](https://mvnrepository.com/) のような検索サイトを使う
    - もしくは、使用したいライブラリの公式ページに書いてある情報を参照する

## 依存関係の解決
- Maven の強力な機能の１つとして、依存関係の解決がある
- pom.xml では、そのプロジェクトが使用するアーティファクトを依存関係として次のように定義できる

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>
  </dependencies>
</project>
```

- `<dependencies>` の下の `<dependency>` が、１つのアーティファクトを指している
- このように宣言すると、 Maven はプロジェクトをビルドするときに自動的に `<dependencies>` で宣言したアーティファクトをリモートリポジトリからダウンロードしてきてくれて、クラスパスに追加してくれる
    - つまり、ライブラリを手で落としてくる手間が省ける
- さらに、リポジトリに格納されたアーティファクトには、それ自身の pom.xml も格納されている
    - もしプロジェクトで指定したアーティファクトの pom.xml に、さらに `<dependencies>` があった場合、 Maven はその依存アーティファクトも自動的にダウンロードしてくれる
    - つまり、芋づる式にすべての依存関係を解決してくれる仕組みになっている
- Maven は、この仕組みのおかげで依存ライブラリの管理が非常に楽になっている
    - Maven の前身である Apache Ant は依存関係を手動で管理しなければならかったので、非常に辛かった
    - この仕組みは、 Maven の後発である Gradle でも利用されている

## ローカルリポジトリ
- リモートリポジトリからダウンロードしてきたアーティファクトやメタ情報（pom.xml とか）は、 Maven を実行したマシンのローカルにキャッシュされる
- このキャッシュ先のことを、**ローカルリポジトリ**と呼ぶ
- 毎回ビルドのたびにリモートリポジトリにアクセスしていると、時間がかかるしネットワークが使用できない環境ではビルドもできなくなってしまう
- したがって、 Maven はまず先にローカルリポジトリを検索するようになっている
    - ローカルリポジトリに目的のアーティファクトが存在しない場合に、リモートリポジトリに検索に行くようになっている
- リモートリポジトリでアーティファクトが見つかった場合は、それをダウンロードし、ローカルリポジトリに保存する
    - これにより、２回目以降はローカルリポジトリを参照すれば良くなるので、ネットワークアクセスなしでもプロジェクトをビルドできる
- ローカルリポジトリの場所は、デフォルトでは `%USERPROFILE%\.m2\repository` になる
    - Linux 系の OS なら `$HOME/.m2/repository`
- ローカルリポジトリにキャッシュされたアーティファクトは、何もしなければ残り続ける
    - ディスク容量が足りなくなったなどの理由がない限りは、特に消す必要もない

## プライベートリポジトリ
- [Nexus Repository OSS](https://www.sonatype.com/nexus-repository-oss) という OSS のアプリケーションを使用すると、独自のリモートリポジトリを構築できる
    - Nexus はセントラルリポジトリを管理している Sonatype 社によって開発されている
    - 有償版と無償の OSS 版がある
- 例えば、社内だけで共有したいアーティファクトがある場合に、イントラネット内で Nexus サーバーを構築すれば簡単にプライベートリポジトリとして利用できる
- 使用するリモートリポジトリは、 pom.xml で次のように指定できる
    - ホスト名やポート番号は実際の環境に合わせて要調整

```xml:リモートリポジトリを指定した場合のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <repositories>
    <repository>
      <id>my-private-repository</id>
      <name>My Private Repository</name>
      <url>http://private.repository.host/repository</url>
      <layout>default</layout>
    </repository>
  </repositories>
</project>
```

- さらに、 Nexus は他のリモートリポジトリのプロキシとしての機能も持っている
    - 例えば、次のような構成をとっていたとする
        - Nexus リポジトリを、セントラルリポジトリのプロキシとして構築する
        - Nexus リポジトリをリモートリポジトリとして pom.xml で設定する
    - プロジェクトに依存アーティファクトがある場合、 Maven はまずローカルリポジトリを検索する
    - ローカルリポジトリにない場合は、次にリモートリポジトリとして指定した Nexus リポジトリを検索する
    - Nexus リポジトリにもない場合は、 Nexus がセントラルリポジトリを検索する
    - そして、セントラルリポジトリからダウンロードしてきたアーティファクトを Maven に返してくれる
    - このとき、 Nexus リポジトリはセントラルリポジトリからダウンロードしたアーティファクトを内部にキャッシュする
    - もし同じアーティファクトに対する検索リクエストが来た場合は、キャッシュしたアーティファクトを返すようになる
    - これにより、ネットワークトラフィックを抑えたり、何らかの障害でインターネットが繋がらなくなっても、イントラネット内の Nexus リポジトリにさえつながれば依存関係の解決ができるようになる

**プロキシのイメージ**

![maven.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/da66551b-4448-732b-1b69-042138388b05.jpeg)

# プラグイン（基本）
Maven は、すべての処理が**プラグイン(Plugin)**によって行われる。

例えば、 Java のソースコードをコンパイルするための処理は [maven-compiler-plugin](https://maven.apache.org/plugins/maven-compiler-plugin/) によって提供されている。

Maven プロジェクト自身が提供する基本的なプラグインは、[Maven – Available Plugins](https://maven.apache.org/plugins/index.html) で一覧を確認できる。

## プラグインのゴール(Goal)
- プラグインに定義された個々のタスク（処理）のことを**ゴール(Goal)**と呼ぶ
- 使用したいプラグインがどのようなゴールを持つかは、それぞれのプラグインのドキュメントを参照する
    - [maven-jdeps-plugin](https://maven.apache.org/plugins/maven-jdeps-plugin/#Goals_Overview) に定義されているゴール
        - [jdkinternals](https://maven.apache.org/plugins/maven-jdeps-plugin/jdkinternals-mojo.html)
        - [test-jdkinternals](https://maven.apache.org/plugins/maven-jdeps-plugin/test-jdkinternals-mojo.html)
    - [maven-jar-plugin](https://maven.apache.org/plugins/maven-jar-plugin/#Goals_Overview) に定義されているゴール
        - [jar](https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html)
        - [test-jar](https://maven.apache.org/plugins/maven-jar-plugin/test-jar-mojo.html)

## ゴールの実行方法
プラグインのゴールを実行する方法は、大きく次の２つがある。

1. コマンドラインで直接指定して実行する
2. フェーズに紐付けて実行する

２つ目のフェーズに紐付ける方法は置いておいて、先に１の直接指定する方法について確認する。

### 完全修飾名指定
```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

```text:jdepsプラグインを実行する
> mvn org.apache.maven.plugins:maven-jdeps-plugin:3.1.2:jdkinternals
...
classes -> java.base
   example          -> java.io            java.base
   example          -> java.lang          java.base
...
```

- 何やら長ったらしい引数を mvn コマンドに渡している
- これは、先程の [maven-jdeps-plugin](https://maven.apache.org/plugins/maven-jdeps-plugin/) の完全修飾名とゴール名を指定している
- 書式にすると `<完全修飾名>:<ゴール名>` といった感じ
    - `<完全修飾名>` は、ここでは `org.apache.maven.plugins:maven-jdeps-plugin:3.1.2` で、
    - `<ゴール名>` は、 `jdkinternals` になる
- 完全修飾名指定の場合は、その情報を元にリポジトリからプラグインの jar ファイルが取得され、ゴールが実行される

### prefix 指定（プラグインの設定が無い場合）
- 毎回完全修飾名を入力するのは辛いので、以下のような省略した記述もできるようになっている

```text:省略した場合
> mvn jdeps:jdkinternals
...
classes -> java.base
   example          -> java.io            java.base
   example          -> java.lang          java.base
...
```

- こちらは、 `<prefix>:<ゴール名>` という書式の指定方法になる
    - 完全修飾名指定なのか prefix 指定なのかは、コロン (`:`) の数で区別されている[^4]
        - `:` が３つなら、 `groupId:artifactId:version:goal` という完全修飾名での指定
        - `:` が２つなら、 `groupId:artifactId:goal` というバージョン番号抜きでの指定
        - `:` が１つなら、 `prefix:goal` というプレフィックスでの指定
- prefix 指定の場合、次のような感じで完全修飾名が解決される
- まず、 `groupdId` を以下のいずれかとする
    - `org.apache.maven.plugin`
    - `org.codehaus.mojo`
- 次に、 `groupId` ごとにメタデータ (`maven-metadata.xml`) を調べる
    - [org.apache.maven.plugin の maven-metadata.xml](https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml)
    - [org.codehaus.mojo の maven-metadata.xml](https://repo.maven.apache.org/maven2/org/codehaus/mojo/maven-metadata.xml)
- `maven-metadata.xml` の中身を見ると、 `artifactId` ごとに prefix の対応が列挙されているのがわかる

```xml:org.apache.maven.pluginのmaven-metadata-xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <plugins>
    <plugin>
      <name>Apache Maven ACR Plugin</name>
      <prefix>acr</prefix>
      <artifactId>maven-acr-plugin</artifactId>
    </plugin>
    ...
    <plugin>
      <name>Apache Maven JDeps Plugin</name>
      <prefix>jdeps</prefix>
      <artifactId>maven-jdeps-plugin</artifactId>
    </plugin>
    ...
  </plugins>
</metadata>
```

- この `maven-metadata.xml` の中からコマンドラインで指定された prefix と一致するものを見つけ、その `<artifactId>` を `artifactId` とする
    - コマンドラインで指定した prefix は `jdeps` なので、 `maven-jdeps-plugin` が `artifactId` になる
- 次は、 `artifactId` ごとのメタデータ(`maven-metadata.xml`)を調べる
    - [maven-jdeps-plugin の maven-metadata.xml](https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jdeps-plugin/maven-metadata.xml)

```xml:maven-jdeps-pluginのmaven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jdeps-plugin</artifactId>
  <versioning>
    <latest>3.1.2</latest>
    <release>3.1.2</release>
    <versions>
      <version>3.0.0</version>
      <version>3.1.0</version>
      <version>3.1.1</version>
      <version>3.1.2</version>
    </versions>
    <lastUpdated>20190619052916</lastUpdated>
  </versioning>
</metadata>
```

- リリースバージョン（`<release>`）に設定されている値を `version` とする
    - リリースバージョンがなければ、最新バージョン（`<latest>`）[^5] を `version` とする
- 以上で決定した `groupId`, `artifactId`, `version` を、完全修飾名とする
    - `groupId` : `org.apache.maven.plugin`
    - `artifactId` : `maven-jdeps-plugin`
    - `version` : `3.1.2`


[^4]: 厳密には `compiler::::compile` のような指定の場合もプレフィックス指定と判定されるけど、ここはわかりやすさを優先してコロンの数としている（詳しくは [MojoDescriptorCreator の実装を参照](https://github.com/apache/maven/blob/maven-3.6.3/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoDescriptorCreator.java#L141)）

[^5]: `<release>` はリリースバージョン内での最新なのに対して、 `<latest>` はスナップショットも含めて最新のバージョンを指している

### プラグインを明示的に設定する
- ↑の方法はメタデータを元にプラグインのバージョンが決まっている
    - おそらく新しいバージョンが出たら最新版を使うことになるが、動きを固定できないとビルドが不安定になる恐れがある
    - したがって、通常はプロジェクトごとに使用するプラグインのバージョンを固定する
- また、↑の場合 prefix 指定ができるのは `groupId` が `org.apache.maven.plugin` か `org.codehaus.mojo` のいずれかの場合に限られる[^6]
    - これら以外の `groupId` のプラグインを prefix 指定で利用したい場合は、プラグインを明示的に設定する必要がある

[^6]: 厳密には `settings.xml` の `<pluginGroups>` で検索対象の `groupId` を追加できる（参考：[Introduction to Plugin Prefix Resolution](https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html#Configuring_Maven_to_Search_for_Plugins)）

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.asciidoctor</groupId>
        <artifactId>asciidoctor-maven-plugin</artifactId>
        <version>1.5.8</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- 個々のプラグインは、 `<plugin>` タグで設定する
    - `<plugin>` は、 `<build><plugins>` の下に列挙する
    - `<groupId>`, `<artifactId>`, `<version>` で、プラグインの完全修飾名を指定する
- ここでは [Asciidoctor の Maven プラグイン](https://github.com/asciidoctor/asciidoctor-maven-plugin) を設定している
    - この設定により、 `asciidoctor-mavne-plugin` のバージョンは `1.5.8` に固定されることになる

```text:asciidoctor-maven-pluginを実行している様子
> mvn asciidoctor:process-asciidoc
...
[INFO] BUILD SUCCESS
...
```

### prefix 指定の解決（プラグインの指定がある場合）
- ↑のように `<plugin>` でプラグインが明示されている場合、 prefix 指定から完全修飾名を解決する方法が少し変わる
- まず Maven は、 pom.xml で明示的に設定されている各プラグインの `plugin.xml` を確認していく
    - 普通、 `plugin.xml` はプラグインの jar 内の `META-INF/maven` の下に格納されている
- 例えば、 `asciidoctor-mavne-plugin` の `plugin.xml` は次のようになっている

```xml:asciidoctor-maven-pluginのplugin.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- Generated by maven-plugin-tools 3.5 on 2019-03-30 -->

<plugin>
  <name>Asciidoctor Maven Plugin and Doxia Parser</name>
  <description>Asciidoctor Maven Plugin and Doxia Parser (for Maven Site integration)</description>
  <groupId>org.asciidoctor</groupId>
  <artifactId>asciidoctor-maven-plugin</artifactId>
  <version>1.5.8</version>
  <goalPrefix>asciidoctor</goalPrefix>
  <isolatedRealm>false</isolatedRealm>
  <inheritedByDefault>true</inheritedByDefault>
  ...
```

- ここで重要なのは、 `<goalPrefix>` に設定されている値になる
    - この値がコマンドラインで指定された prefix と等しいプラグインの `groupId` と `artifactId` が採用される
- 最後に、 `version` は次のように決まる
    - pom.xml で指定されていれば、そのバージョンが採用される
    - 指定されていない場合は、 `artifactId` ごとのメタデータ(`maven-metadata.xml`)から決定される（プラグイン指定なしの場合と同じ方法）

### artifactId と prefix の関係
- ここまでの説明で、 `artifactId` と prefix には、本質的には関連がないことがわかる
    - `maven-metadata.xml` の `<prefix>` か、 `plugin.xml` の `<goalPrefix>` で prefix が決まっている
- しかし、実際に存在するプラグインたちには、 `artifactId` と prefix に次のような関係がある
    - Maven プロジェクトが提供している公式プラグインの場合
        - `artifactId` が `maven-XXX-plugin` の場合、 `XXX` が prefix となる
    - その他のプラグインの場合
        - `artifactId` が `XXX-maven-plugin` の場合、 `XXX` が prefix となる
- これらは、そういう命名ルールが推奨されている結果であって、この名前じゃないとプラグインが作れないわけではない
    - 作ろうと思えば、この命名ルールとは全く異なる `artifactId` のプラグインを作ることもできる
    - **ただし、 `maven-XXX-plugin` という命名は、それが Maven の公式プラグインであること表すためのものなので、公式以外がこの命名を使うと商標侵害になるので要注意**
        - [Important Notice: Plugin Naming Convention and Apache Maven Trademark](https://maven.apache.org/guides/plugin/guide-java-plugin-development.html#Important_Notice:_Plugin_Naming_Convention_and_Apache_Maven_Trademark)
    - 一般的でない命名をしたところで利用者が混乱するだけでメリットがないので、プラグインを自作するなら特別な理由がない限りは `XXX-maven-plugin` という `artifactId` にしておくのが無難
- ということで、 `artifactId` と prefix は本質的には関連しないが、実用上は関連しているものと思って問題ない（と思う）
    - なので、 `artifactId` が `foo-maven-plugin` なら、 prefix は `foo` と思っていい（と思う）
    - 逆に、 prefix が `foo` なら、 `artifactId` は `foo-maven-plugin` と思っていい（と思う）（Maven プロジェクトが提供しているプラグインでない場合）

## プラグインの設定（パラメータ）
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>Hello World!!</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn antrun:run
...
main:
     [echo] Hello World!!
...
```

- プラグインの設定（**パラメータ**）は、 `<plugin>` の下の `<configuration>` タグで記述できる
- `<configuration>` の下に指定できるタグは、プラグインのゴールごとに異なる
    - ここでは [maven-antrun-plugin](https://maven.apache.org/plugins/maven-antrun-plugin/) というプラグインを使用している
    - このプラグインには [run](https://maven.apache.org/plugins/maven-antrun-plugin/run-mojo.html) というゴールがある
    - `run` ゴールの説明ページに、 `<configuration>` で指定可能なパラメータが載っているのがわかる
- 複数のゴールを持つプラグインの場合、 `<configuration>` に指定したパラメータはすべてのゴールに対して適用されることになる

### プラグインの説明を確認する
- プラグインにどういうゴールがあり、どういうパラメータが指定できるのかは、個々のプラグインの説明ページを見れば確認できる
- しかし、いちいちページを開くのが面倒なときは [maven-help-plugin](https://maven.apache.org/plugins/maven-help-plugin/) で確認することもできる

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <configuration>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-antrun-plugin</artifactId>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn help:describe
...
Name: Apache Maven AntRun Plugin
Description: Runs Ant scripts embedded in the POM
Group Id: org.apache.maven.plugins
Artifact Id: maven-antrun-plugin
Version: 1.8
Goal Prefix: antrun

This plugin has 2 goals:

antrun:help
  Description: Display help information on maven-antrun-plugin.
    Call mvn antrun:help -Ddetail=true -Dgoal=<goal-name> to display parameter
    details.

antrun:run
  Description: Maven AntRun Mojo.
    This plugin provides the capability of calling Ant tasks from a POM by
    running the nested Ant tasks inside the <tasks/> parameter. It is
    encouraged to move the actual tasks to a separate build.xml file and call
    that file with an <ant/> task.

For more information, run 'mvn help:describe [...] -Ddetail'
...
```

- [describe](https://maven.apache.org/plugins/maven-help-plugin/describe-mojo.html) というゴールを実行することで、 `<configuration>` で指定したプラグインの説明を確認できる
    - `<groupId>` と `<artifactId>` でプラグインを `maven-antrun-plugin` に特定できるようにしている
- `describe` ゴールの説明ページを見るとわかるが、 `<goal>` でゴールを絞ったり `<detail>` で詳細（ゴールごとに指定できるパラメータ）の出力もできる


```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <configuration>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-antrun-plugin</artifactId>
          <goal>run</goal>
          <detail>true</detail>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn help:describe
...
antrun:run
  Description: Maven AntRun Mojo.
    ...
  Implementation: org.apache.maven.plugin.antrun.AntRunMojo
  Language: java

  Available parameters:

    ...

    target
      The XML for the Ant target. You can add anything you can add between
      <target> and </target> in a build.xml.

    ...
...
```

- ↑の例は省略しているが、全てのパラメータが説明付きで出力される

### システムプロパティで指定する
- `help:describe` の `<configuration>` で指定していたパラメータは、システムプロパティから指定することもできる

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn help:describe -Dplugin=antrun
...
Name: Apache Maven AntRun Plugin
Description: Runs Ant scripts embedded in the POM
Group Id: org.apache.maven.plugins
Artifact Id: maven-antrun-plugin
Version: 1.8
Goal Prefix: antrun

This plugin has 2 goals:

antrun:help
  Description: Display help information on maven-antrun-plugin.
    ...

antrun:run
  Description: Maven AntRun Mojo.
    ...

For more information, run 'mvn help:describe [...] -Ddetail'
...
```

- Maven を実行するときのシステムプロパティとして `-Dplugin=antrun` を指定している
    - `plugin` パラメータの説明は [ドキュメント](https://maven.apache.org/plugins/maven-help-plugin/describe-mojo.html#plugin) を参照
- このように、ゴールのパラメータの中にはシステムプロパティで値を渡すことができるものがある
    - すべてのパラメータがシステムプロパティで指定できるわけではない
- あるパラメータがシステムプロパティで指定できるかどうかは、そのパラメータのドキュメントに **User property** が書かれているかどうかで確認できる
    - 例えば、 [plugin パラメータのドキュメント](https://maven.apache.org/plugins/maven-help-plugin/describe-mojo.html#plugin) では、 `User Property: plugin` と書かれているのが確認できる
    - これは、 `plugin` という名前のプロパティで値を指定できることを表している
    - パラメータ名とプロパティ名は常に一致している**わけではない**
        - `plugin` はたまたま一致しているだけで、 [antrun の skip](https://maven.apache.org/plugins/maven-antrun-plugin/run-mojo.html#skip) のように一致していないものもある
- ここで**プロパティ**と言っているのは、システムプロパティに限らず pom.xml 上の `<properties>` でも指定できることを表している

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <properties>
    <plugin>jdeps</plugin>
  </properties>

  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<properties>` で、 `plugin` に `jdeps` を設定している

```text:実行結果（システムプロパティでpluginを指定しない場合）
> mvn help:describe
...
Name: Apache Maven JDeps Plugin
Description: The JDeps Plugin uses the jdeps tool to analyze classes for
  internal API calls.
Group Id: org.apache.maven.plugins
Artifact Id: maven-jdeps-plugin
Version: 3.1.2
Goal Prefix: jdeps
...
```

```text:実行結果（システムプロパティでpluginを指定した場合）
> mvn help:describe -Dplugin=antrun
...
Name: Apache Maven AntRun Plugin
Description: Runs Ant scripts embedded in the POM
Group Id: org.apache.maven.plugins
Artifact Id: maven-antrun-plugin
Version: 1.8
Goal Prefix: antrun
...
```

- システムプロパティを指定していない場合は、 `<properties>` で指定した値(`jdeps`)が採用された
- システムプロパティを指定した場合は、そちらの値(`antrun`)が採用された

# ライフサイクルとフェーズ
- 「ビルド」と一口に言っても、その内容は様々な処理を含んでいる
- 例えば、簡単な Java プログラムをビルドするには、次のような処理が考えられる
    1. ソースコードをコンパイルする
    2. リソースファイルを収集する
    3. テストコードをコンパイルする
    4. テスト用のリソースファイルを収集する
    5. テストコードを実行する
    6. コンパイル結果とリソースファイルをまとめて、 jar などのアーカイブにパッケージングする
    7. アーカイブを所定の場所に格納する
- Maven では、これら１つ１つの処理を**フェーズ(Phase)**と呼ぶ
- そして、一連のフェーズのセットを**ライフサイクル(Lifecycle)**と呼ぶ

## 組み込みのライフサイクル
- Maven には、標準で次の３つのライフサイクルが用意されている
    - `default`
    - `clean`
    - `site`

### default ライフサイクル
- `default` ライフサイクルは、プロジェクトをビルドしてデプロイするまでのライフサイクルを定義している
- `default` ライフサイクルは、次のフェーズで構成されている
    1. `validate`
    2. `initialize`
    3. `generate-sources`
    4. `process-sources`
    5. `generate-resources`
    6. `process-resources`
    7. `compile`
    8. `process-classes`
    9. `generate-test-sources`
    10. `process-test-sources`
    11. `generate-test-resources`
    12. `process-test-resources`
    13. `test-compile`
    14. `process-test-classes`
    15. `test`
    16. `prepare-package`
    17. `package`
    18. `pre-integration-test`
    19. `integration-test`
    20. `post-integration-test`
    21. `verify`
    22. `install`
    23. `deploy`


### clean ライフサイクル
- `clean` ライフサイクルは、プロジェクトの成果物を削除するライフサイクルを定義している
- `clean` ライフサイクルは、次のフェーズで構成されている
    1. `pre-clean`
    2. `clean`
    3. `post-clean`


### site ライフサイクル
- `site` ライフサイクルは、プロジェクトの Web サイト生成のライフサイクルを定義している
- `site` ライフサイクルは、次のフェーズで構成されている
    1. `pre-site`
    2. `site`
    3. `post-site`
    4. `site-deploy`

## フェーズとプラグインのゴールの関係
- 各ライフサイクルのフェーズには、そのフェーズで実行するプラグインのゴールが紐付けられている

### clean, site ライフサイクルの場合
- 例えば、 `clean` ライフサイクルと `site` ライフサイクルでは、次のようにゴールが紐付けられている

**clean プラグイン**

|フェーズ     |プラグイン           |ゴール |
|------------|--------------------|-------|
|`pre-clean` |-                   |-      |
|`clean`     |`maven-clean-plugin`|`clean`|
|`post-clean`|-                   |-      |


**site プラグイン**

|フェーズ      |プラグイン          |ゴール  |
|-------------|-------------------|--------|
|`pre-site`   |-                  |-       |
|`site`       |`maven-site-plugin`|`site`  |
|`post-site`  |-                  |-       |
|`site-deploy`|`maven-site-plugin`|`deploy`|

- つまり、 `clean` フェーズを実行すると、 `maven-clean-plugin` の `clean` ゴールが実行されることになる

### default ライフサイクルの場合
- `default` ライフサイクルの場合、フェーズとゴールの紐付けは固定ではない
- `default` ライフサイクルで実行されるゴールは、そのプロジェクトの **packaging** によって変化する

#### packaging
- packaging とは、そのプロジェクトをどのようにパッケージ化するかを決める設定値で、次のいずれかの値が指定できる
    - `pom`
    - `jar`
    - `ejb`
    - `maven-plugin`
    - `war`
    - `ear`
    - `rar`
- この packaging は、 pom.xml 上で次のように指定する

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <packaging>jar</packaging>
  ...
</project>
```

- ここでは、 packaging として `jar` を指定している
- なお、 `<packaging>` タグは pom.xml で省略することができる
    - 省略した場合、デフォルトは `jar` になる

#### packaging ごとのゴール
- `default` ライフサイクルのフェーズに紐付けられているゴールは、 packaging によって次のように異なる
- 一例として、 `pom` と `jar` に紐付けられたゴールは次のようになっている
    - フェーズの数が多いので、何も紐付けられていないフェーズは除外している（`validate` など）
    - これら以外の packaging については [Maven Core – Plugin Bindings for Default Lifecycle Reference](https://maven.apache.org/ref/current/maven-core/default-bindings.html) などを参照

**pom**

|フェーズ      |プラグイン             |ゴール    |
|-------------|----------------------|----------|
|`install`    |`maven-install-plugin`|`install` |
|`deploy`     |`maven-deploy-plugin` |`deploy`  |


**jar**

|フェーズ                  |プラグイン               |ゴール          |
|-------------------------|------------------------|----------------|
|`process-resources`      |`maven-resources-plugin`|`resources`     |
|`compile`                |`maven-compiler-plugin` |`compile`       |
|`process-test-resources` |`maven-resources-plugin`|`testResources` |
|`test-compile`           |`maven-compiler-plugin` |`testCompile`   |
|`test`                   |`maven-surefire-plugin` |`test`          |
|`package`                |`maven-jar-plugin`      |`jar`           |
|`install`                |`maven-install-plugin`  |`install`       |
|`deploy`                 |`maven-deploy-plugin`   |`deploy`        |

## フェーズを実行する
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <groupId>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

```text:testフェーズを実行する
> mvn test
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello ---
...
```

- フェーズは、 mvn コマンドの引数に指定することで実行できる
- ここでは、 packaging が `jar` のプロジェクトで `test` フェーズを実行してみている
    - `<packaing>` の記述が省略されているので、 packaing はデフォルトの `jar` になる
- フェーズを指定して実行した場合、そのフェーズより前のフェーズもすべて順番に実行される
    - 例えば、 `test` フェーズの前には `validate` や `process-resources`, `test-compile` といったフェーズが定義されている
    - 上の例では `maven-resources-plugin:resources`, `maven-compiler-plugin:compile`, ... と実行されていっていることがわかる
- あるフェーズが実行されると、そのフェーズに紐付いているゴールがすべて実行される
    - `maven-resources-plugin:resources` ゴールは `resources` フェーズに、  
      `maven-compiler-plugin:compile` ゴールは `compile` フェーズに紐付けらている

## フェーズやゴールを複数指定して実行する
```text
> mvn clean compiler:compile
```

- フェーズやゴールは、 mvn コマンドに複数指定して実行できる
- この場合の実行順序は、 `clean` → `compiler:compile` の順序になる
- つまり、引数で指定した順番で実行されるようになっている

## フェーズにゴールを紐付ける
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <phase>validate</phase>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <target>
            <echo>Hello Antrun!!</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn compile
...
[INFO] --- maven-antrun-plugin:1.8:run (default) @ hello ---
[INFO] Executing tasks

main:
     [echo] Hello Antrun!!
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
```

- 任意のフェーズに対して、任意のゴールを紐付けることができる
- フェーズとゴールの紐付けは、プラグインの設定の中で `<execution>` で行う
- `<phase>` でフェーズを指定し、 `<goals><goal>` で紐付けるゴールを指定する
- ここでは、 `validate` フェーズに対して `maven-antrun-plugin` の `run` ゴールを紐付けている
- `<goals><goal>` という構造からも分かる通り、１つのフェーズに対して同じプラグインの複数のゴールを紐付けることもできる

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jdeps-plugin</artifactId>
        <version>3.2.6</version>
        <executions>
          <execution>
            <phase>verify</phase>
            <goals>
              <goal>jdkinternals</goal>
              <goal>test-jdkinternals</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn verify
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello ---
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello ---
...
[INFO] --- maven-jdeps-plugin:3.1.2:jdkinternals (default) @ hello ---
...
[INFO] --- maven-jdeps-plugin:3.1.2:test-jdkinternals (default) @ hello ---
```

- `maven-jdeps-plugin` の `jdkinternals` と `test-jdkinternals` を `verify` フェーズに紐づけている
- １つのフェーズに複数のゴールが紐付けられている場合、それらのゴールは pom.xml 上で宣言されている順番で実行される[^7]
    - つまり、↑の設定の場合は `jdkinternals` → `test-jdkinternals` の順番で実行される
    - `<goal>` タグの順序を変えれば、実行順序を変更できる

[^7]: [In Maven 2.0.5 and above, multiple goals bound to a phase are executed in the same order as they are declared in the POM,](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Build_Lifecycle_Basics#A_Build_Phase_is_Made_Up_of_Plugin_Goals)

### 複数のフェーズに紐付ける
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <id>foo</id>
            <phase>validate</phase>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <id>bar</id>
            <phase>compile</phase>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <target>
            <echo>Hello Antrun!!</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn compile
...
[INFO] --- maven-antrun-plugin:1.8:run (foo) @ hello ---
...
     [echo] Hello Antrun!!
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-antrun-plugin:1.8:run (bar) @ hello ---
...
     [echo] Hello Antrun!!
...
```

- `<execution>` タグを複数書けば、複数のフェーズに対してゴールの紐付けができるようになる
- `<execution>` を複数指定する場合は、 `<id>` タグも指定しなければならない
    - `<id>` には、その `<execution>` を識別できる一意な値を設定する
    - この値は、実行時のコンソールに出力されている(`maven-antrun-plugin:1.8:run (foo)` の `(foo)` の部分)
    - どの `<execution>` が実行されたのかを識別するための情報になるので、ビルドがうまく行かないときなどのデバッグに役立つ
    - したがって、識別しやすいわかりやすい名前を付けておくのがいい

### execution ごとに設定する
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <id>foo</id>
            <phase>validate</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <echo>VALIDATE!!</echo>
              </target>
            </configuration>
          </execution>
          <execution>
            <id>bar</id>
            <phase>compile</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <echo>COMPILE!!</echo>
              </target>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn compile
...
[INFO] --- maven-antrun-plugin:1.8:run (foo) @ hello ---
...
     [echo] VALIDATE!!
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-antrun-plugin:1.8:run (bar) @ hello ---
...
     [echo] COMPILE!!
...
```

- ゴールの設定を記述する `<configuration>` は、 `<execution>` ごとにも指定できる
- これにより、特定のフェーズごとに設定を分けるといったことが可能になる

### ゴールを直接実行したときに採用される特別なid
```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <id>default-cli</id>
            <configuration>
              <target>
                <echo>Hello @ default-cli</echo>
              </target>
            </configuration>
          </execution>
          <execution>
            <id>validate-phase</id>
            <phase>validate</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <echo>Hello @ validate-phase</echo>
              </target>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn antrun:run
...
     [echo] Hello @ default-cli
...

> mvn validate
...
     [echo] Hello @ validate-phase
...
```

- ゴールを直接実行した場合、 `id` はデフォルトで `default-cli` になる
- したがって、この値で `<execution><id>` を設定しておけば、ゴールを直接実行したときにだけ適用される設定を記述できる

### id を指定して実行する
- Maven の 3.3.1 からは、 `<execution>` の `<id>` を指定してゴールを実行することができるようになっている

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <id>default-cli</id>
            <configuration>
              <target>
                <echo>Hello default-cli!!</echo>
              </target>
            </configuration>
          </execution>
          <execution>
            <id>foo</id>
            <configuration>
              <target>
                <echo>Hello Foo!!</echo>
              </target>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

- `default-cli` と `foo` の、２つの `<id>` で `<execution>` を定義している

```text:実行結果
> mvn antrun:run
...
     [echo] Hello default-cli!!

> mvn antrun:run@foo
...
     [echo] Hello Foo!!
```

- `antrun:run` だけで実行した場合は、 `default-cli` のほうが実行される
- `antrun:run@foo` のように、ゴール指定の後ろに `@<id>` と続けることで、指定された `<id>` の `<execution>` が実行される


### デフォルトのフェーズ
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>3.1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>check</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

```text:実行結果
> mvn verify
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello ---
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello ---
...
[INFO] --- maven-checkstyle-plugin:3.1.1:check (default) @ hello ---
```

- [maven-checkstyle-plugin](https://maven.apache.org/plugins/maven-checkstyle-plugin/) を追加して `verify` フェーズを実行している
- pom.xml では、 `<execution>` で `<goal>check</goal>` しか指定しておらず、 `<phase>` の指定はしていない
- しかし、実行結果を見ると `verify` フェーズで `check` ゴールが実行されていることがわかる
- ゴールは、デフォルトで紐づくフェーズを定義できるようになっている
    - [check ゴールの説明ページ](https://maven.apache.org/plugins/maven-checkstyle-plugin/check-mojo.html) を見ると、デフォルトフェーズが `verify` と書かれているのがわかる
- もし `<execution>` でフェーズが明示されていない場合は、このデフォルトで紐付いているフェーズで実行されるようになっている
- あるゴールにフェーズが１つも紐付いていない場合、そのゴールはコマンドラインで直接指定しなければ起動できない

# プラグインのバージョン指定
- プラグインは、 `<plugin>` で明示的に設定しなくてもある程度使用できる
    - packaging の設定によって、いくつかの基本的なプラグイン（`maven-compiler-plugin` など）は自動的に適用される
    - プレフィックス指定された場合は、 `groupId`, `artifactId`, `version` が自動解決される
- 設定がなくても動くのであれば、わざわざ `<plugin>` を書かなくても良いような気がする
- しかし、 `<plugin>` の設定無しで実行する方法は、プラグインのバージョンが実行のたびに変わる可能性がある
- 例えば、 packaging によって自動設定されるプラグインは、 Maven のバージョンによって変化する可能性がある
    - たとえば、 3.6.3 で設定されるプラグインのバージョンは[ここ](https://maven.apache.org/ref/3.6.3/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging)で確認できる
    - 開発者の使っている Maven のバージョンが揃っていないと、開発者ごとにビルド結果が変わるかもしれない
    - （そもそも、デフォルトで設定されるプラグインのバージョンは古い）
- また、プレフィックス指定で解決されるプラグインのバージョンは、基本的にリポジトリの最新になる
    - これも、実行時の最新が変われば結果も変化する恐れがある
- したがって、使用するプラグインのバージョンは、面倒だと思っても基本的に明示するのが良い
- ただし、ここに記載している pom.xml の例は、以下の理由によりプラグインの設定を省略していることがある
    - 記述量が多くなると、それだけで読む気が失せる
    - 必要な部分（今説明しようとしている部分）に注目できない

# プロジェクトの親子関係
- プロジェクトには親子関係をもたせることができる

```text:フォルダ構成
|-pom.xml
`-child/
  `-pom.xml
```

- トップフォルダに親プロジェクトの pom.xml を置き、 `child` フォルダの下に子プロジェクトの pom.xml を置いている

```xml:/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <groupId>example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>artifactId = ${project.artifactId}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- 親プロジェクトの pom.xml では、 `<packaging>` に `pom` を指定しなければならない

```xml:/child/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>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child</artifactId>
</project>
```

- 子プロジェクトの pomx.ml では、親を指定するために `<parent>` を使用する
- `<parent>` では、親POM を特定するために `groupId`, `artifactId`, `version` を指定する
- 親子関係のあるプロジェクトでは、子の POM は親の POM を継承する
- このため、子の pom.xml に記述されていない設定は、親の pom.xml の設定が引き継がれることになる
    - `<groupId>` や `<version>` なども親の設定が引き継がれるため、子の pom.xml では記述が不要になる
    - ただし、 `<artifactId>` はさすがに指定しなければならない

```text:実行結果（トップフォルダ）
> mvn antrun:run
...
     [echo] artifactId = parent
```

```text:実行結果（childフォルダ）
> mvn antrun:run
...
     [echo] artifactId = child
```

- `<plugins>` の設定も引き継がれているため、親プロジェクトで設定された `antrun` プラグインを子プロジェクトでも使用できている

## 親プロジェクトの解決方法
- `<parent>` で設定された親の pom.xml は、次の順序で検索される
    1. `<relativePath>` が指定されている場合は、その場所の pom.xml を参照
    2. `<relativePath>` が指定されていない場合は、**１つ上の階層**の pom.xml を参照
    3. それも無ければ、リポジトリ（ローカル or リモート）を検索して参照
- ↑の例は親プロジェクトが子プロジェクトの１つ上だったため、特に場所の指定をしなくても親 POM が見つかっていた

### 親プロジェクトの場所を明示する
- しかし、↓のようなフォルダ構成の場合は、 `<relativePath>` によるパスの指定が必要になる

```text:フォルダ構成
|-parent/
| `-pom.xml
`-child/
  `-pom.xml
```

- `parent` と `child` が横並びになっており、子プロジェクトの１つ上に親プロジェクトの pom.xml が存在しない状態になっている
- この状態で子プロジェクトで Maven を実行すると、次のようなエラーになる

```text:実行結果（子プロジェクト）
> mvn antrun:run
...
[FATAL] Non-resolvable parent POM for example:child:1.0.0: Failure to find example:parent:pom:1.0.0 in https://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced and 'parent.relativePath' points at wrong local POM @ line 6, column 11
 @
...
```

- これを解決するには、リポジトリに親プロジェクトの jar を登録するか、以下のように `<relativePath>` で親プロジェクトの場所を明示してあげる必要がある

```xml:/child/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
    <relativePath>../parent/pom.xml</relativePath>
  </parent>

  <artifactId>child</artifactId>
</project>
```

```text:実行結果（子プロジェクト）
> mvn antrun:run
...
     [echo] artifactId = child
```

- 無事親プロジェクトが見つかって動作した

## POMのマージ
- POMに親子関係がある場合、子のPOMには親のPOMがマージされる
- マージがどのように行われるのか確認してみる

```xml:親の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>example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <properties>
    <hoge>PARENT</hoge>
    <fuga>PARENT</fuga>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>parent/dir</directory>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>hoge = ${hoge}</echo>
            <echo>fuga = ${fuga}</echo>
            <echo>piyo = ${piyo}</echo>
            <echo>dependencies[0].artifactId = ${project.dependencies[0].artifactId}</echo>
            <echo>dependencies[1].artifactId = ${project.dependencies[1].artifactId}</echo>
            <echo>resources[0].directory = ${project.build.resources[0].directory}</echo>
            <echo>resources[1].directory = ${project.build.resources[1].directory}</echo>
            <echo>plugins[0] = ${project.build.plugins[0].id}</echo>
            <echo>plugins[1] = ${project.build.plugins[1].id}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- 検証のために、いくつかの設定を記述し、 `antrun` プラグインで設定内容を表示できるようにしている

```xml:子の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>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child</artifactId>

  <properties>
    <fuga>CHILD</fuga>
    <piyo>CHILD</piyo>
  </properties>

  <dependencies>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.14</version>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>child/dir</directory>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jdeps-plugin</artifactId>
        <version>3.1.2</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- 親POM を継承し、いくつかの設定が重なるように記述している

```text:実行結果（親プロジェクト）
> mvn antrun:run
...
     [echo] hoge = PARENT
     [echo] fuga = PARENT
     [echo] piyo = ${piyo}
     [echo] dependencies[0].artifactId = commons-lang3
     [echo] dependencies[1].artifactId = ${project.dependencies[1].artifactId}
     [echo] resources[0].directory = parent/dir
     [echo] resources[1].directory = ${project.build.resources[1].directory}
     [echo] plugins[0] = org.apache.maven.plugins:maven-antrun-plugin:1.8
     [echo] plugins[1] = ${project.build.plugins[1].id}
```

- まずは、親プロジェクトで各設定値を出力してみる
- 当然親POM で設定した値だけが出力され、設定されていない値は式が評価できずにそのまま出力されている

```text:実行結果（子プロジェクト）
> mvn antrun:run
...
     [echo] hoge = PARENT
     [echo] fuga = CHILD
     [echo] piyo = CHILD
     [echo] dependencies[0].artifactId = commons-codec
     [echo] dependencies[1].artifactId = commons-lang3
     [echo] resources[0].directory = child/dir
     [echo] resources[1].directory = ${project.build.resources[1].directory}
     [echo] plugins[0] = org.apache.maven.plugins:maven-antrun-plugin:1.8
     [echo] plugins[1] = org.apache.maven.plugins:maven-jdeps-plugin:3.1.2
```

- 子プロジェクトで出力した結果、単一値の設定で重なる部分（`<properties><fuga>` など）は子POM の設定が採用されている
    - 一方、複数値の設定で重なる部分（`<dependencies>` や `<plugins>` など）は、親POM をベースにして子POM の要素が追加されている
- 基本的に、単一値の項目は上書きされ、複数値の項目 (`<plugins>`など) は要素が追加される
    - ただし一部例外があり、例えば `<build><resources>` は複数値項目だが上書きされている
    - [公式ドキュメント](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Project_Inheritance)では、 `<resources>` もマージの対象と書かれているけど実際は上書きされる
    - 実装的には [ModelMerger](https://github.com/apache/maven/blob/maven-3.6.3/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java) というクラスで POM のマージが行われているっぽい
        - このクラスの [mergeBuildBase_Resources()](https://github.com/apache/maven/blob/maven-3.6.3/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java#L2195) で `<resources>` のマージが行われる
        - この実装では、 `<resource>` は追加する形でマージされることになっている
    - しかし、実際にはこの `ModelMerger` のサブクラスである [MavenModelMerger](https://github.com/apache/maven/blob/maven-3.6.3/maven-model-builder/src/main/java/org/apache/maven/model/merge/MavenModelMerger.java) でマージが行われているっぽい
        - `MavenModelMerger` は [mergeBuildBase_Resources()](https://github.com/apache/maven/blob/maven-3.6.3/maven-model-builder/src/main/java/org/apache/maven/model/merge/MavenModelMerger.java#L381) をオーバーライドしていて、 `target` （子POM）の `<resources>` が空の場合のみ親POMの内容をマージするようにしている
        - つまり、子POMに `<resources>` が存在すればマージは行われず、子POMの内容だけが採用される（結果として上書きの動作になる）
    - `<resources>` 以外は、この `MavenModelMerger` の実装を見てみるしかない？

### マージ後の POM を確認する
- POM には親子関係があり、複数の POM を親に持つような場合は末端の POM を見ただけでは最終的な状態がわからない
- 特にビルドがうまく動作しないときは、 POM が期待通りにマージされているか確認したくなることがよくある
- そういうときは、 `maven-help-plugin` の [effective-pom](https://maven.apache.org/plugins/maven-help-plugin/effective-pom-mojo.html) ゴールが利用できる

```text:effective-pomゴールを実行する
> mvn help:effective-pom
...
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>
  <groupId>example</groupId>
  <artifactId>child</artifactId>
  <version>1.0.0</version>
（長いので省略）
...
```

- `effective-pom` ゴールを実行すると、Super POM を含めてすべての親POM の情報がマージされた最終的な POM が出力される
- これを見ることで、そのプロジェクトの設定が実際どうなっているのかを確認できる

## configurations のマージ
- プラグインの設定を記述する `<configurations>` 以下は、プラグインごとに設定が異なるため、固定のスキーマ定義を持っていない
- したがって、 `<configurations>` のマージは一定のルールに沿って画一的に行われる
    - 例えば、 `<execution>` なら `<id>` で識別ができるように定義が固まっているので、同じ `<id>` をマージするといったことができる
    - しかし、 `<configurations>` の下のタグはそういう識別ができないので、だいたい同じ位置のタグをそれっぽくマージする感じになる
        - （正確な仕様や実装を確認したわけではないので、そういう雰囲気というくらいの気持ちで）
- どういうふうにマージされるのか、実際の例で見てみる

```xml:親POM
      ...
        <configuration>
          <persons>
            <person>
              <name>Taro</name>
              <age>18</age>
            </person>
            <person>
              <name>Hanako</name>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

```xml:子POM
      ...
        <configuration>
          <persons>
            <person>
              <sex>male</sex>
            </person>
            <cat>nya-</cat>
            <person>
              <name>Ayako</name>
              <age>15</age>
            </person>
            <person>
              <name>Rin</name>
              <age>12</age>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

この状態で `help:effective-pom` でマージ結果を確認する。

```xml:effective-pom
      ...
        <configuration>
          <persons>
            <person>
              <sex>male</sex>
              <name>Taro</name>
              <age>18</age>
            </person>
            <cat>nya-</cat>
            <person>
              <name>Ayako</name>
              <age>15</age>
              <sex>female</sex>
            </person>
            <person>
              <name>Rin</name>
              <age>12</age>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

- 言葉で説明するのは難しいが、なんかいい感じにマージされているのが分かると思う
    - おそらく、同じ位置に同名のタグがあれば、その中身を再帰的にマージしていっている感じだと思う

### マージの仕方を制御する
- デフォルトの動作でも、そこそこよしなにマージしてくれる気がする
- しかし、それだと色々問題があるという場合は、デフォルトのマージの挙動を変更することもできる

```xml:親POM
      ...
        <configuration>
          <persons>
            <person>
              <name>Taro</name>
              <age>18</age>
            </person>
            <person>
              <name>Hanako</name>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

- 親POMは変更なし

```xml:子POM
      ...
        <configuration>
          <persons>
            <person combine.self="override">
              <sex>male</sex>
            </person>
            <cat>nya-</cat>
            <person combine.children="append">
              <name>Ayako</name>
              <age>15</age>
            </person>
            <person>
              <name>Rin</name>
              <age>12</age>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

- `combile.self="override"` と `combine.children="append"` という属性を追加している

```xml:effective-pom
      ...
        <configuration>
          <persons>
            <person combine.self="override">
              <sex>male</sex>
            </person>
            <cat>nya-</cat>
            <person combine.children="append">
              <name>Hanako</name>
              <sex>female</sex>
              <name>Ayako</name>
              <age>15</age>
            </person>
            <person>
              <name>Rin</name>
              <age>12</age>
              <sex>female</sex>
            </person>
          </persons>
        </configuration>
      ...
```

- `combine.*` の属性を追加した要素だけ、マージのされかたが変わっているのが分かる
- `combine.self="override"` を指定した場合、親POMの要素は完全に捨てられて子POMの要素だけが使用される
- `combine.children="append"` を指定した場合、親POMの要素の末尾に子POMの要素を単純に追加する形になる
- これら２つの属性を子POMで使用することで、マージの仕方をある程度調整できるようになる
- ただし、これらの属性は、この属性を記述した要素にだけ効果がある
    - 入れ子の要素にまで伝播することはない
    - 入れ子の要素で動きを変更したい場合は、別途入れ子の要素でも同じように `combine.*` 属性を指定する必要がある

## 親プロジェクトで定義だけをまとめる
- 親プロジェクトの設定は、常に子プロジェクトに継承される
- すべての子プロジェクトで共通な設定の場合は便利だが、一部の子プロジェクトでだけ必要な設定の場合は、余計な継承が発生してしまう
- そこで、定義だけを親プロジェクトにまとめて、実際の適用はその定義を使用したい子プロジェクトで明示的に行う方法が用意されている

### プラグインの定義をまとめる
```xml:親の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>example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.8</version>
          <executions>
            <execution>
              <phase>validate</phase>
              <goals><goal>run</goal></goals>
              <configuration>
                <target>
                  <echo>Hello ${project.artifactId}</echo>
                </target>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
```

- 親プロジェクトでプラグインの定義をまとめるには、 `<pluginManagement>` を使用する
- この下は、通常のプラグイン定義と同じように `<plugins>` を記述できる
- ただし、ここで記述したものはあくまで設定を定義しただけで、プロジェクトには適用されていない

```xml:子１のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child1</artifactId>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
```

- 実際にプロジェクトに適用するには、 `<groupId>`, `<artifactId>` を適用したいプロジェクトのプラグイン定義に記述する
    - バージョンを省略した場合は、 `<pluginManagement>` に記述したものが使用される
- 子プロジェクトで個別の設定を追加することも可能


```xml:子２のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child2</artifactId>
</project>
```

- こちらは、特に何もプラグインを適用していない

---

```text:実行結果（子１）
> mvn validate
...
[INFO] ---------------------------< example:child1 >---------------------------
[INFO] Building child1 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-antrun-plugin:1.8:run (default) @ child1 ---
[INFO] Executing tasks

main:
     [echo] Hello child1
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
```

- 親POMで定義された設定で、プラグインが適用されている

```text:実行結果（子２）
> mvn validate
...
[INFO] ---------------------------< example:child2 >---------------------------
[INFO] Building child2 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
```

- プラグインは何も適用されていない

### 依存関係の定義をまとめる
```xml:親の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>example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>
  </dependencies>
</project>
```

- 依存関係の定義を親にまとめるには、 `<dependencyManagement>` を使用する
- この下は、通常の `<dependencies>` 同様の記述が可能
- `<pluginManagement>` 同様、ここでの定義は宣言のみで、プロジェクトへの適用は個別に行う


```xml:子１のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child1</artifactId>

  <dependencies>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
    </dependency>
  </dependencies>
</project>
```

- 子プロジェクトでの適用は、依存関係に `<groupId>` と `<artifactId>` を記述して行う
- `<version>` を省略した場合は、 `<dependencyManagement>` で指定したものが使用される
    - 子プロジェクトで独自に `<version>` を上書き指定することも可能


```xml:子２のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child2</artifactId>
</project>
```

- こちらは、特に依存関係を何も定義していない

---

```xml:実行結果（子１のeffective-pom）
> mvn help:effective-pom
...
  <dependencies>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
...
```

- 親POMで設定したバージョンで `commons-io` が適用されている


```xml:実行結果（子２のeffective-pom）
> mvn help:effective-pom
...
  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
...
```

- `commons-io` の依存関係は適用されていない

# プロジェクトの集約(Aggregation)
- 親子関係は子が親を参照する形だった
- 一方で、プロジェクトの集約では逆に親が子を参照する形になる
- プロジェクトを集約すると、親プロジェクトでまとめてゴールやフェーズを指定して実行できるようになる

```text:フォルダ構成
|-pom.xml
`-sub/
  `-pom.xml
```

- トップフォルダが集約元となるプロジェクト
- `child` フォルダの下に、集約対象となるプロジェクトが存在する

```xml:集約元の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>example</groupId>
  <artifactId>root</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <modules>
    <module>sub</module>
  </modules>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>Hello ${project.artifactId}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- プロジェクトを集約するには、集約元となるプロジェクトで `<modules>` を使用する
- `<modules>` の下に、集約対象となるプロジェクトを `<module>` で列挙していく
- 集約元となるプロジェクトの `<packaging>` は、 `pom` にしておく必要がある

```xml:集約対象のpom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <groupId>example</groupId>
  <artifactId>sub</artifactId>
  <version>1.0.0</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>Hello ${project.artifactId}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- 集約対象の pom.xml は普通に書く
- この POM は集約元のプロジェクトを継承しているわけではないので、 `<groupId>` の記述などは個別にしている

```text:実行結果（集約元プロジェクトで実行）
> mvn antrun:run
...
main:
     [echo] Hello sub
...
main:
     [echo] Hello root
...
```

- 集約元プロジェクトでプラグインのゴールを実行すると、集約対象のプロジェクトでも同じゴールが実行されている
- このように、プロジェクトを集約すると、集約元プロジェクトで実行したコマンドが集約先のプロジェクトでも実行されるようになる
- 全プロジェクトをまとめてビルドしたいときなどは、この仕組があると便利

## 集約と親子関係を組み合わせる
- 集約と親子関係の仕組みは、組み合わせることができる（というか、普通は組み合わせて使用する）

```text:フォルダ構成
|-pom.xml
`-child/
  `-pom.xml
```

```xml:親の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>example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <modules>
    <module>child</module>
  </modules>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>Hello ${project.artifactId}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- 親POMの構成は、とくに変化無し

```xml:子の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>

  <parent>
    <groupId>example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>child</artifactId>
</project>
```

- `<parent>` で親プロジェクトを宣言
- これにより、プラグインの設定は親の設定を継承できるようになり、記述が不要になった

```text:実行結果（親プロジェクトで実行）
> mvn antrun:run
...
main:
     [echo] Hello parent
...
main:
     [echo] Hello child
...
```

- 親子関係と集約を同時に適用できている


# Java プロジェクトのビルド
- packaging が `jar` のプロジェクトがどのようにビルドされるのかを読み解いていく。

## default ライフサイクルで実行されるゴール
- 普通に Java をビルドするプロジェクトを作る場合、 packaging はデフォルトの `jar` になる
- したがって、 `default` ライフサイクルで実行されるゴールは以下になる
    1. `resources:resources`
    2. `compiler:compile`
    3. `resources:testResources`
    4. `compiler:testCompile`
    5. `surefire:test`
    6. `jar:jar`
    7. `install:install`
    8. `deploy:deploy`
- 各ゴールが何をしているのか、１つずつ見ていく

## リソースを収集する
- 最初に実行されるのは、 `maven-resources-plugin` の [resources ゴール](https://maven.apache.org/plugins/maven-resources-plugin/resources-mojo.html) となっている

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

- 最小構成の pom.xml で検証する

```text:フォルダ構成
|-pom.xml
`-src/main/
  |-java/
  | `example/
  |  `-App.java
  `-resources/
    |-hoge.txt
    `-foo/
      `-bar.txt
```

- `src/main/resources` 以下に適当にファイルやフォルダを配置している

```text:コマンド実行
> mvn resources:resources
...
[INFO] --- maven-resources-plugin:2.6:resources (default-cli) @ hello ---
...
[INFO] BUILD SUCCESS
```

- `resources:resources` ゴールを直接実行

```text:実行後のフォルダ構成
|-pom.xml
|-src/main/
| |-java/
| | `example/
| |  `-App.java
| `-resources/
|   |-hoge.txt
|   `-foo/
|     `-bar.txt
`-target/classes/
  |-hoge.txt
  `-foo/
    `-bar.txt
```

- `target/classes` フォルダの下に、 `src/main/resources` 以下の内容がコピーされている
- `maven-resources-plugin` は、プロジェクトのリソースフォルダを出力先フォルダにコピーする処理を提供する
- リソースフォルダと出力先フォルダは、デフォルトでは次のようになっている
    - リソースフォルダ：`src/main/resources`
    - 出力先フォルダ：`target/classes`
- リソースフォルダは、 `${project.build.resources}` に設定されているフォルダが対象となる
    - この値は、 Super POM で `${project.basedir}/src/main/resources` が設定されている
- 出力先のフォルダは、 `resources` ゴールの `outputDirectory` パラメータで指定された場所となっている
    - このパラメータには、デフォルトで `${project.build.outputDirectory}` が設定されている
    - そして、この値は Super POM によってデフォルトで `${project.build.directory}/classes` が設定されている
    - さらに、 `${project.build.directory}` には `${project.basedir}/target` が設定されている
- ということで、例えば次のように pom.xml を書くことで動作をカスタマイズできる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <build>
    <resources>
      <resource>
        <directory>src/main/hoge</directory>
      </resource>
    </resources>
  </build>
</project>
```

- リソースフォルダを `src/main/hoge` に変更している

```text:フォルダ構成
|-pom.xml
`-src/main/
  |-hoge/
  | |-fizz.txt
  | `-hoge/
  |   `-fuga.txt
  |
  `-resources/
    |-buzz.txt
    `-foo/
      `-bar.txt
```

- `src/main/resources` と `src/main/hoge` の２つを用意している

```text:resourcesゴールを実行
> mvn resources:resources
...
```

```text:実行結果
|-pom.xml
|-src/main/
| |-hoge/
| | |-fizz.txt
| | `-hoge/
| |   `-fuga.txt
| |
| `-resources/
|   |-buzz.txt
|   `-foo/
|     `-bar.txt
|
`-target/classes/
  |-fizz.txt
  `-hoge/
    `-fuga.txt
```

- `src/main/hoge` 以下だけがコピーされるように変わっている

---

- 整理すると `resources:resources` ゴールは、デフォルトで次のように動作する
    - `${project.basedir}/src/main/resources` 以下のファイルやフォルダを、 `${project.basedir}/target/classes` の下にコピーする
    - リソースフォルダは、 `<project><build><resources>` で設定できる
    - 出力先フォルダは、 `<project><build><outputDirectory>` で設定できる


## Java ソースをコンパイルする
- 次に実行されるゴールは、 [maven-compiler-plugin](https://maven.apache.org/plugins/maven-compiler-plugin/) の [compile](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html) ゴールとなる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

- 最小構成の POM で検証する

```text:フォルダ構成
|-pom.xml
`-src/main/java
  `-example/
    `-Hello.java
```

- `Hello.java` だけを配置したシンプルな構成

```text:compileゴールを実行
> mvn compiler:compile
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-cli) @ hello ---
...
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] ソース・オプション5は現在サポートされていません。6以降を使用してください。
[ERROR] ターゲット・オプション1.5は現在サポートされていません。1.6以降を使用してください。
```

- エラーで落ちた
- コンパイル時のオプションである [-source](https://docs.oracle.com/javase/jp/11/tools/javac.html#GUID-AEEC9F07-CB49-4E96-8BC7-BCC2C7F725C9__GUID-B4EE2436-E146-428D-A3CB-E0DAE27BA5B7) および [-target](https://docs.oracle.com/javase/jp/11/tools/javac.html#GUID-AEEC9F07-CB49-4E96-8BC7-BCC2C7F725C9__GUID-89CBDEA1-9FF4-4EA0-AB7D-B418FA2C3DE9) に指定されているバージョンが `5`, `1.5` で古すぎるというエラー内容になっている
- JDK 11 では、 `-source`, `-target` に 5 以下のバージョンは[指定できなくなっている](https://docs.oracle.com/javase/jp/11/migrate/index.html#JSMIG-GUID-77874D97-46F3-4DB5-85E4-2ACB5F8D760B)
- packaging が `jar` の場合、デフォルトで適用される `maven-compiler-plugin` のバージョンは [3.1 となっている](https://maven.apache.org/ref/3.6.3/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging)
- `maven-compiler-plugin` の 3.1 では、 `-source` および `-target` のバージョン指定は、デフォルトで [1.5 となっている](https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.1/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java#L120)
    - ちなみに、確認時最新の `maven-compiler-plugin` の 3.8.1 では、 [1.6](https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.8.1/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java#L100) がデフォルトとなっている
- ということで、このエラーを解消するには、 `maven-compiler-plugin` のバージョンを上げるか、 `source`, `target` の指定を上げる必要がある
- 動作を確定させるためにも、両方とも指定しておくのが無難

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `maven-compiler-plugin` で `source` を指定するには、 [source パラメータ](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source) を設定する
    - `<configuration>` で指定することもできるが、 `maven.compiler.source` プロパティでも設定できるようになっている
    - ここでは、プロパティを使った設定を使用している（こっちの方が一般的？）
- `target` も同様で、 `maven.compiler.target` プロパティで設定できるようになっている

```text:実行結果
> mvn compiler:compile
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-cli) @ hello ---
...
[WARNING] File encoding has not been set, using platform encoding MS932, i.e. build is platform dependent!
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...
```

- コンパイルはできたが、なにやら警告が出ている
- 警告内容は、ソースファイルのエンコーディングが環境デフォルトに依存している、というもの
- ソースファイルのエンコーディングの指定は、 [encoding パラメータ](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#encoding) で行う
    - 説明に書いているように、デフォルトでは `${project.build.sourceEncoding}` を使用するようになっている
    - しかし、この値は Super POM でも設定されていないので、結果的に環境デフォルト（Windows なら MS932 など）となっている
- ということで、エンコーディングも設定してみる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `${project.build.sourceEncoding}` という記述から、 `<project><build>` の下に `<sourceEncoding>` という要素を記述するのかなというイメージが湧くが、実際はプロパティを宣言する（<del>誰が分かるねん</del>）
    - ちなみに、 `encoding` というプロパティでも設定可能となっているが、ネットで調べるとだいたい `project.build.sourceEncoding` の方法が引っかかる
    - `encoding` だと、 Java ソース以外のエンコーディングにも適用される可能性があるとか、そういうのがあるのかな？（適当）

```text:実行結果
> mvn compiler:compile
...
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-cli) @ hello ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

- 無事、警告もなくコンパイルが成功した

```text:出力結果
|-pom.xml
|-src/main/java/
| :
`-target/
  |-classes/
  : `-example/
      `-Hello.class
```

- コンパイル結果は、 `target/classes` の下に出力されている
    - 出力先の設定は、 [compile ゴール](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html) のドキュメント上には記載されていない
    - これは、 `compile` ゴールの実装クラスである [outputDirectory](https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.8.1/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java#L76) フィールドで宣言されている
    - デフォルト値は `${project.build.outputDirectory}` となっている
    - `readonly=true` が設定されているため、このフィールドを外部から直接設定することはできないようになっている
- ソースフォルダは、 `src/main/java` がデフォルトで使用される
    - こちらも、ドキュメント上は記載がない
    - 実装上は、 [compileSourceRoots](https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.8.1/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java#L70) というフィールドで宣言されている
    - `defaultValue` を見ると、 `${project.compileSourceRoots}` が設定されていることがわかる
    - しかし、この `project.compileSourceRoots` というプロパティを pom.xml 上で参照しようとしても、値を確認することはできない
    - `compileSourceRoots` は [MavenProject](https://github.com/apache/maven/blob/maven-3.6.3/maven-core/src/main/java/org/apache/maven/project/MavenProject.java#L131) クラスのフィールドとして宣言されている
    - このフィールドは、 [DefaultProjectBuilder](https://github.com/apache/maven/blob/maven-3.6.3/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java#L691) によって値が設定されている
    - この実装から、 `project.build.sourceDirectory` が `compileSourceRoots` に設定されていることが分かる
    - そして、 `project.build.sourceDirectory` には、 Super POM によって `${project.basedir}/src/main/java` が設定されている
    - この仕組みを見ると、ソースフォルダを複数設定できないことが予想できる
        - 複数のソースフォルダを設定したい場合は、 [build-helper-maven-plugin](https://www.mojohaus.org/build-helper-maven-plugin/) というプラグインを導入する必要がある

---

- 整理すると、 `compiler:compile` では次のことが行われる
    - `${project.basedir}/src/main/java` の下にある Java ソースコードをコンパイルして、 `${project.basedir}/target/classes` に出力する
    - javac の `-source`, `-target` オプションは、 `maven.compiler.source`, `maven.compiler.target` プロパティで指定できる
    - エンコーディングの指定は、 `project.build.sourceEncoding` プロパティで指定できる
    - ソースフォルダは `<project><build><sourceDirectory>` で指定できる
        - 複数のフォルダを指定したい場合は、 `build-helper-maven-plugin` が必要
    - 出力先フォルダは、 `<project><build><outputDirectory>`

## テスト用のリソース収集とコンパイル
- 次に実行されるのは、テスト用のリソースを収集する [resources:testResources](https://maven.apache.org/plugins/maven-resources-plugin/testResources-mojo.html) と、  
  テスト用のソースコードをコンパイルする [compiler:testCompile](https://maven.apache.org/plugins/maven-compiler-plugin/testCompile-mojo.html) になる
- それぞれの基本的な動作は `resources:resources`, `compiler:compile` と同じなので、ざっくりとだけ

```text:フォルダ構成
|-pom.xml
`-src/test/
  |-java/
  | `-example/
  |   `-HelloTest.java
  |
  `-resources/
    |-foo/
    | `-bar.txt
    `-fizz.txt
```

- `src/test/java` の下にテスト用のソースコードを、  
  `src/test/resources` の下にテスト用のリソースファイルを配置する

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- pom.xml は、 `compiler:compile` のときと同じ

```text:実行
> mvn resources:testResources compiler:testCompile
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-cli) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-cli) @ hello ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
```

```text:実行結果
|-pom.xml
|-src/test/
| :
`-target/
  `-test-classes/
    |-fizz.txt
    |-foo/
    | `-bar.txt
    `-example/
      `-HelloTest.class
```

- `target/test-classes` の下に結果が出力されている

---

- 整理すると、次のような動作になっている
- `testResources` ゴールは、 `${project.basedir}/src/test/resources` 以下のファイルやフォルダを、 `${project.basedir}/target/test-classes` にコピーする
    - リソースフォルダは `<project><build><testResources>` で指定できる
- `testCompile` ゴールは、 `${project.basedir}/src/test/java` 以下の Java ソースファイルをコンパイルして、 `${project.basedir}/target/test-classes` に出力する
    - ソースフォルダは、 `<project><build><testSourceDirectory>` で指定できる

## テストを実行する
- 次は、コンパイルしたテストコードを実行するため、 [maven-surefire-plugin](https://maven.apache.org/surefire/maven-surefire-plugin/) の [test ゴール](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html) が実行される。

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.6.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- JUnit5 を動かすために以下の修正を入れている
    - `junit-jupiter` を依存関係に追加
    - `maven-surefire-plugin` のバージョンに 2.22.2 を指定
        - JUnit5 を使うには 2.22.0 以上を指定する必要がある

```text:フォルダ構成
|-pom.xml
`-src/
  |-main/java/
  | `-example/
  |   `-Hello.java
  `-test/java/
    `-example/
      `-HelloTest.java
```

- テスト対象のクラス（`Hello.java`）と、テストクラス（`HelloTest.java`）を配置

```text:実行
> mvn test
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ hello ---
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running example.HelloTest
...
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.027 s - in example.HelloTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

> dir /b target\surefire-reports
example.HelloTest.txt
TEST-example.HelloTest.xml
```

- `surefire:test` ゴールは `test` フェーズに紐付いているので、 `test` フェーズを指定して実行している
    - `surefire:test` を単独で実行することも可能だが、その場合はソースのコンパイルは別途済ませておく必要がある
- 実行対象となるテストクラスは [includes パラメータ](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#includes) で指定されており、デフォルトで次のクラスが対象になる
    - `**/Test*.java`
    - `**/*Test.java`
    - `**/*Tests.java`
    - `**/*TestCase.java`
- テストの結果は `target/surefire-reports` の下にテキストと xml 形式で出力される
    - これは、 `surefire:test` ゴールの [reportsDirectory パラメータ](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#reportsDirectory) で指定されている（デフォルトは `${project.build.directory}/surefire-reports`）

### テストをスキップする
- `test` フェーズの次はプロジェクトのコンパイル結果を jar に固める `package` フェーズが実行される
- つまり、 jar を生成するためには `test` フェーズが正常終了する必要がある
    - `test` フェーズでテストが失敗した場合は、 `package` フェーズは実行されない
- テストが通っていないコードを jar に固めても使い物にならない、というのが本来あるべき姿だが、実際はテストコードがメンテされておらずテストが通らないというプロジェクトも残念ながら存在する
- そういうときは、 `test` フェーズだけ実行をスキップして `package` フェーズを実行するということがよく行われる（本来はすべきではない）

```text:実行結果
> mvn -DskipTests package
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello ---
[INFO] Tests are skipped.
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello ---
...
```

- `surefire:test` ゴールには [skipTests](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#skipTests) というパラメータが用意されている
- これが設定されていると、テストの実行がスキップされるようになる

---

- 整理すると、 `test` フェーズでは次のことが行われる
    - `maven-surefire-plugin` によってテストコードが実行される
    - JUnit 5 を使用するためには、 `maven-surefire-plugin` のバージョンを 2.22.0 以上にしておく必要がある
    - 実行対象となるテストクラスは [includes パラメータ](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#includes) で指定されている
    - テストの結果は `target/surefire-reports` の下に出力される（[reportsDirectory パラメータ](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#reportsDirectory)）
    - テストの実行は [skipTests パラメータ](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#skipTests) を設定することでスキップできる
        - ただし、これの使用は必要最小限に留めるべき
            - テストがメンテナンスされておらず、今すぐ jar が必要（本来はテストが通るように修正するのが正しい対応）
            - テストは通るが、実行に時間がかかるため毎回実行してられない
            - etc

## jar に固める
- コンパイル結果を jar に固める処理は、 [maven-jar-plugin](https://maven.apache.org/plugins/maven-jar-plugin/) の [jar ゴール](https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html) によって行われる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- コンパイルで必要になる最小限の設定にしている

```text:フォルダ構成
|-pom.xml
`-src/
  |-test/
  | :
  `-main/
    |-java/
    | `-example/
    |   `-Hello.java
    `-resources/
      `-hoge.txt
```

```text:packageフェーズを実行
> mvn package
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ hello ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
...
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ hello ---
...
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ hello ---
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

- `jar:jar` ゴールは `package` フェーズに紐付いているので、 `package` フェーズを実行している

```text:実行結果
|-pom.xml
|-src/
| :
`-target/
  |-hello-1.0.0.jar
  :
```

- `target` フォルダの直下に、 `hello-1.0.0.jar` が出力されている
    - jar ファイルの出力先は、 jar ゴールの [outputDirectory](https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html#outputDirectory) パラメータによって設定されている
        - このパラメータのデフォルトは、 `${project.build.directory}` となっている（`${project.basedir}/target`）
    - また、 jar ファイルの名前は jar ゴールの実装では [finalName](https://github.com/apache/maven-jar-plugin/blob/maven-jar-plugin-3.2.0/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java#L80) というフィールドで宣言されている
        - このフィールドは `readonly = true` なので、外から直接変更することはできない
        - このフィールドの値には、 `${project.build.finalName}` という値が設定されている
        - この値は Super POM によって `${project.artifactId}-${project.version}` がデフォルトで設定されている
        - したがって、 jar の名前を変更したい場合は、 `<project><build><finalName>` を設定すればいい
- この jar を解凍すると、中身は次のようになっている

```text:hello-1.0.0.jarの中身
|-hoge.txt
|-example/
| `-Hello.class
`-META-INF/
  |-MANIFEST.MF
  `-maven/example/hello/
    |-pom.properties
    `-pom.xml
```

- `hoge.txt` と `Hello.class` は、それぞれ `resources:resources`, `compiler:compile` によって `target/classes` の下に出力されたものが梱包されている
    - どのフォルダの内容が jar に梱包されるかは、 jar ゴールの [classesDirectory](https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html#classesDirectory) パラメータによって設定されている
        - このパラメータのデフォルトは、 `${project.build.outputDirectory}` となっている（`${project.basedir}/target/classes`）
- `META-INF` の下に pom.xml が出力されているが、その内容はこのプロジェクトの pom.xml と同じものが入っている
- `MANIFEST.MF` と `pom.properties` は、それぞれ次のような内容になっている

```text:MANIFEST.MF
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.6.3
Built-By: xxxxx
Build-Jdk: 11.0.6
```

```properties:pom.properties
#Generated by Maven
#Sun Mar 29 21:59:48 JST 2020
groupId=example
artifactId=hello
version=1.0.0
```

- これらは、 jar ゴールの [archive](https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html#archive) パラメータで設定する
- `archive` の設定の詳細は [Apache Maven Archiver – Reference](http://maven.apache.org/shared/maven-archiver/index.html) で確認できる
- デフォルトで有効になっている設定で、これらのファイルが生成されている

---

- 整理すると、 `jar` ゴールは次のように動作する
- ファイル名
    - `${project.artifactId}-${project.version}`
    - 変更する場合は `<project><build><finalName>` で指定
- 出力場所
    - `${project.basedir}/target`
    - 変更する場合は `<project><build><directory>` で指定
- 梱包対象
    - `${project.basedir}/target/classes`
    - 変更する場合は、 `<project><build><outputDirectory>` か `jar` ゴールの `classesDirectory` パラメータで指定
    - さらに `archive` パラメータで `MANIFEST.MF` や `META-INF` 以下に追加される情報を設定できる


## ローカルリポジトリにインストールする
- `install` フェーズに紐付けられている [maven-install-plugin](https://maven.apache.org/plugins/maven-install-plugin/) の [install ゴール](https://maven.apache.org/plugins/maven-install-plugin/install-mojo.html) は、ローカルリポジトリにプロジェクトの成果物をインストールする機能を提供する

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
  ...
</project>
```

```text:installを実行
> mvn install
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
```

- `install:install` ゴールは `install` フェーズに紐付いているので、 `install` フェーズを実行している（ややこしい）
- 実行が完了したら、ローカルリポジトリの中を確認する
    - ローカルリポジトリの場所は、 Windows ならデフォルトは `%USERPROFILE%\.m2\repository` (Linux系のOSなら `$HOME/.m2/repository`)
    - `settings.xml` で別の場所を指定している場合は、そちら

```text:ローカルリポジトリ
ローカルリポジトリ/
 |-example/
 : `-hello/
     |-1.0.0/
     : |-hello-1.0.0.jar
       :
```

- ローカルリポジトリの中に jar ファイルが保存されている
- 登録される jar ファイルは、多分 `package` フェーズで生成される jar ファイル
    - `install:install` ゴールのドキュメントでは `project's main artifact` と書かれていたが、具体的に何が `project's main artifact` になるのか、明確に書かれているところを見つけることはできなかった
    - 実装的には [MavenProject の getArtifact()](https://github.com/apache/maven/blob/maven-3.6.3/maven-core/src/main/java/org/apache/maven/project/MavenProject.java#L215) で取得できる [Artifact の getFile()](https://github.com/apache/maven/blob/maven-3.6.3/maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java#L85) で取得できるファイルがインストールされているっぽいところまでは読み解けたが、この `File` オブジェクトが設定されているところを見つけきれなかった
- ローカルリポジトリにインストールされたアーティファクトは、ローカルの別のプロジェクトで参照できるようになる

```xml: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>example</groupId>
  <artifactId>foo</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>example</groupId>
      <artifactId>hello</artifactId>
      <version>1.0.0</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `hello-1.0.0.jar` への依存関係を `<dependencies>` で宣言している

```java:Foo.java
package example;

public class Foo {
    public static void main(String... args) {
        new Hello().hello();
    }
}
```

- `Hello` クラスを使った実装を書いている

```text:プロジェクトをコンパイル
> mvn compile
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ foo ---
...
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ foo ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
```

- コンパイルが通った（`Hello` クラスが解決できている）

---

- 整理すると、 `install` ゴールは次のように動作する
- おそらく `package` フェーズで生成された成果物（jar ファイルなど）を、ローカルリポジトリにインストールする
- インストールされたアーティファクトは、ローカルの他のプロジェクトから依存対象として参照できるようになる

## デプロイする
- 最後に実行されるのが `deploy` フェーズとなる
- `deploy` フェーズでは、 [maven-deploy-plugin](https://maven.apache.org/plugins/maven-deploy-plugin/) の [deploy ゴール](https://maven.apache.org/plugins/maven-deploy-plugin/deploy-mojo.html) が実行される
- `deploy` ゴールは、プロジェクトの成果物をリモートのリポジトリにデプロイする
- <del>環境構築とかが面倒な割に実際に使う機会は少ない気がするので</del>検証は省略

# Web プロジェクトのビルド
- packaging を `war` にした場合に、プロジェクトがどのようにビルドされるかを見る
- ただ、 `default` フェーズで実行されるプラグインは、 `jar` の場合とほとんど違いはない
- `jar` と異なるのは、 `package` フェーズで実行されるゴールが [war](https://maven.apache.org/plugins/maven-war-plugin/) プラグインの [war ゴール](https://maven.apache.org/plugins/maven-war-plugin/war-mojo.html) ということだけ

```text:フォルダ構成
|-pom.xml
`-src/main/
  |-java/
  | `-example/webapp/
  |   `-HelloServlet.java
  `-webapp/
    `-WEB-INF/
      `-hello.jsp
```

- `src/main/webapp` というフォルダを作成し、その下に `war` に含める `WEB-INF` などのフォルダを作成している

```xml: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>example</groupId>
  <artifactId>webapp</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.10</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<packaging>` に `war` を指定している
- 依存関係として次の２つを指定している
    - Servlet API
    - Apache Commons Lang3

```text:ビルド
> mvn package
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ webapp ---
...
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ webapp ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ webapp ---
...
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ webapp ---
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ webapp ---
...
[INFO] --- maven-war-plugin:3.2.3:war (default-war) @ webapp ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

- `jar` の場合と同様に、各フェーズでプラグインのゴールが実行されていき、最後に `war:war` が実行されている

```text:出力結果
|-pom.xml
|-src/
| :
`-target/
  `-webapp-1.0.0.war
```

- war ファイルは、 `target` の直下に生成されている
    - 出力位置は `war` ゴールの [outputDirectory パラメータ](https://maven.apache.org/plugins/maven-war-plugin/war-mojo.html#outputDirectory) で指定されており、デフォルトで `${project.build.directory}` となっている
    - また、 war ファイルの名前は [warName](https://github.com/apache/maven-war-plugin/blob/maven-war-plugin-3.2.3/src/main/java/org/apache/maven/plugins/war/WarMojo.java#L71) という読み取り専用のパラメータで指定されており、デフォルトが `${project.build.finalName}` となっている
- `webapp-1.0.0.war` の中身は次のようになっている

```text:webapp-1.0.0.warの中身
webapp-1.0.0.war/
|-WEB-INF/
| |-hello.jsp
| |-classes/
| | `-example/webapp/
| |   `-HelloServlet.class
| `-lib/
|   `-commons-lang3-3.10.jar
`-META-INF/
  |-MANIFEST.MF
  `-maven/example/webapp/
    |-pom.properties
    `-pom.xml
```

- `src/main/java` のコンパイル結果と `src/main/webapp` の下の内容が war ファイルの中に入っているのが分かる
    - class ファイルやライブラリの jar 以外で war の中に入れるファイルは、 `src/main/webapp` フォルダの下に配置する
    - これは `war` ゴールの [warSourceDirectory パラメータ](https://maven.apache.org/plugins/maven-war-plugin/war-mojo.html#warSourceDirectory) で指定されており、デフォルトが `${basedir}/src/main/webapp`  となっている
- また、依存関係に指定していた Apache Commons Lang3 の jar も `WEB-INF/lib` の下に格納されている
    - Servlet API の jar は格納されていない
    - これは、 Servlet API への依存のスコープを `provided` に指定していたため
    - スコープの詳細な説明は後述

---

- 整理すると、 packaging が `war` の場合、プロジェクトは次のようにビルドされる
    - 基本は `jar` と同じようにビルドされる
    - ただし、 `package` フェーズだけ `war:war` ゴールが実行される点が `jar` の場合と異なる
    - `war:war` ゴールでは、プロジェクトのコンパイル結果が war ファイルに固められて出力される
    - war の中には class ファイル・リソース・依存ライブラリに加えて、 `src/main/webapp` の下に配置したファイルもそのまま格納される

# Java アプリケーションを実行する
```java:Hello.java
package example;

import java.util.Arrays;

public class Hello {
    public static void main(String... args) {
        System.out.println("Hello World! args=" + Arrays.toString(args));
    }
}
```

- Hello World して、コマンドライン引数を出力している実装

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
          <execution>
            <id>default-cli</id>
            <configuration>
              <mainClass>example.Hello</mainClass>
              <arguments>
                <argument>one</argument>
                <argument>two</argument>
                <argument>three</argument>
              </arguments>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

- [exec-maven-plugin](https://www.mojohaus.org/exec-maven-plugin/) というプラグインを追加している

```text:実行
> mvn compile exec:java
...
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ hello ---
Hello World! args=[one, two, three]
...
```

- `Hello.java` が実行された
- `exec-maven-plugin` の [java ゴール](https://www.mojohaus.org/exec-maven-plugin/java-mojo.html) を使用すると、プロジェクトのビルド結果をクラスパスに入れて Java プログラムを実行できる
    - `exec-maven-plugin` には任意のコマンドを実行できる [exec ゴール](https://www.mojohaus.org/exec-maven-plugin/exec-mojo.html) も用意されているが、ここでは割愛
- [mainClass](https://www.mojohaus.org/exec-maven-plugin/java-mojo.html#mainClass) パラメータで Main クラスを指定することで、 Java プログラムを実行できる
- [arguments](https://www.mojohaus.org/exec-maven-plugin/java-mojo.html#arguments) パラメータで、コマンドライン引数を渡すことができる
    - `commandlineArgs` というパラメータでも引数を渡すことができるが、こちらは後述するコマンドラインから実行する場合に利用するためにある（たぶん）

## コマンドラインから実行する
- 最初の例は、あらかじめ pom.xml に実行の構成をすべて記述しておく必要がある
- 場合によっては、コマンドラインで引数を色々変更しながら実行したくなることもあるかもしれない
- その場合は、システムプロパティを介してゴールのパラメータを指定する方法をとるとやりやすい

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
          <execution>
            <id>default-cli</id>
            <configuration>
              <mainClass>example.Hello</mainClass>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

- Main クラスをコマンドラインから指定するのは面倒なので、これだけはあらかじめ pom.xml に記述しておいていいと思う

```text:実行結果
> mvn compile exec:java -Dexec.args="ONE TWO THREE"
...
Hello World! args=[ONE, TWO, THREE]
```

- [commandlineArgs](https://www.mojohaus.org/exec-maven-plugin/java-mojo.html#commandlineArgs) パラメータを使えば、引数をスペース区切りの文字列で指定できる
- システムプロパティで指定する場合は、 `exec.args` で指定する

# 依存関係
## 依存関係のスコープ
- 依存関係には、**スコープ(Scope)**というものを設定できる
- スコープとは、その依存関係を使用する範囲を表す
- 例えば、 `compile` スコープに指定されている依存関係は、ソースコードをコンパイルするときからアプリケーションを実行するときまで、常に使用することを表している
- また、 `test` スコープが指定されている依存関係は、テストコード（`src/test/java`）のコンパイルとテストの実行時にだけ使用することを表している
- スコープは、次の6種類存在する
    - `compile`
    - `provided`
    - `runtime`
    - `test`
    - `system`
    - `import`

### compile スコープ
```xml:pom.xml
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.10</version>
</dependency>
```

- スコープを指定していない場合、デフォルトで `compile` スコープが採用される（明示することも可能）
- `compile` スコープは、コンパイルから実行まで、常にその依存関係が必要であることを表している

### provided スコープ
```xml:pom.xml
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>
```

- `provided` スコープは、その依存関係が実行環境から提供される（provided）ことを表している
- 次の条件が満たされる場合に使用する
    - コンパイルやテストでは必要になる
    - しかし、実行時は実行環境が jar を提供してくれるので、アプリケーションが個別に保持しておく必要はない
- 最もよくある例として、 Servlet API が挙げられる
    - Servlet API は、 war をデプロイするアプリケーション・サーバーが提供するものを使用するため、実行時にアプリケーションが個別に依存関係を持っておく必要はない
    - 同様の理由で、 Java EE (Jakarta EE) が提供する他の API (EJB, JAX-RS, etc...)も `provided` で指定する
- `maven-war-plugin` で war を生成した場合、 `provided` に指定された依存関係は `WEB-INF/lib` の下には配置されないようになっている

### runtime スコープ
```xml:pom.xml
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.12</version>
    <scope>runtime</scope>
</dependency>
```

- `runtime` スコープは、 `provided` の逆でコンパイル時には必要ないが実行時に必要な依存関係で使用する
- 典型的な例として、 JDBC ドライバが挙げられる
    - JDBC ドライバを使ってプログラムを書く場合、実装では標準 API が提供している [java.sql.Connection](https://docs.oracle.com/javase/jp/11/docs/api/java.sql/java/sql/Connection.html) などのクラスにだけ依存すればいい
    - 各データベース製品が提供している具象クラスに直接依存することは基本的にはない
    - しかし、当然実行時は実体が必要になる
    - ということで、コンパイル時は必要ないが実行時は必要、というケースに当てはまることになる

### test スコープ
```xml:pom.xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.6.1</version>
    <scope>test</scope>
</dependency>
```

- `test` スコープは、テストソースのコンパイルと実行時にだけ使用する依存関係で使用する
- main のソースコードのコンパイルや、実行時には使用できない
- JUnit のような、テストのときだけ必要になる依存関係で使用する

### system スコープ
```xml:pom.xml
<dependency>
  <groupId>javax.sql</groupId>
  <artifactId>jdbc-stdext</artifactId>
  <version>2.0</version>
  <scope>system</scope>
  <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
```

- `system` スコープは、その依存関係がシステム（実行環境の JRE や JDK）から提供されるものであることを表す
- このスコープは、リポジトリに存在せず JRE や JDK が内部に保持している拡張ライブラリなどを使用することを目的に用意されているものらしい
- `system` スコープを指定した場合は、 `<systemPath>` で対象の jar ファイルのパスを指定する必要がある
- リポジトリに存在しないライブラリをプロジェクトの中に持たせておいて、それを参照するのに使ったりもできる
    - 本来は、プライベートリポジトリを用意して、そこで管理するのが正攻法
- 一応、このスコープの利用は非推奨となっている
    - [System Dependencies](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#System_Dependencies)

### import スコープ
```text:フォルダ構成
|-foo/
| `-pom.xml
`-bar/
  `-pom.xml
```

- foo, bar の２つのプロジェクトが存在する

```xml:pom.xml(foo)
<?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>example</groupId>
  <artifactId>foo</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.10</version>
      </dependency>
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-text</artifactId>
          <version>1.8</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>
```

- foo プロジェクトは packaging を `pom` にして、 `<dependencyManagement>` だけを宣言している
- 依存対象として、次の２つを宣言している
    - `org.apache.commons:commons-lang3:3.10`
    - `org.apache.commons:commons-text:1.8`

```xml:pom.xml(bar)
<?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>example</groupId>
  <artifactId>bar</artifactId>
  <version>1.0.0</version>

  <dependencyManagement>
    <dependencies>
      <dependency>
          <groupId>example</groupId>
          <artifactId>foo</artifactId>
          <version>1.0.0</version>
          <type>pom</type>
          <scope>import</scope>
      </dependency>
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.9</version>
      </dependency>
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-csv</artifactId>
          <version>1.8</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>
```

- bar プロジェクトでは、 `<dependencyManagement>` の中で、先程の foo プロジェクトを `import` スコープで指定している
    - `import` スコープは `<dependencyManagement>` の中でしか指定できない
    - `import` スコープを指定した場合は、合わせて `<type>pom</type>` も指定する必要がある
- また、それ以外の依存対象として、次の２つを宣言している
    - `org.apache.commons:commons-lang3:3.9`
    - `org.apache.commons:commons-csv:1.8`
- この状態で、 bar プロジェクトの `effective-pom` を確認する

```text:effective-pomの確認(bar)
> cd bar

> mvn help:effective-pom
...
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
      </dependency>
      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-csv</artifactId>
        <version>1.8</version>
      </dependency>
      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
```

|artifactId     |foo   |bar  |effective-pom |
|---------------|------|-----|--------------|
|`commons-lang3`|`3.10`|`3.9`|`3.9`         |
|`commons-csv`  |-     |`1.8`|`1.8`         |
|`commons-text` |`1.8` |-    |`1.8`         |


- foo プロジェクトへの依存の宣言が消えて、代わりに foo プロジェクトの `<dependencyManagement>` で宣言していた依存関係が追加されている
- ただし、アーティファクトが重複している場合は bar プロジェクトのバージョンが採用されている（`commons-lang3`）
- このように、 `import` は他の pom プロジェクトの `<dependencyManagement>` を取り込む(importする)特殊なスコープとなっている

#### BOM
- マルチプロジェクトでの依存関係のバージョン管理手法として、 `import` スコープを利用した **BOM (bill of materials)** という方法が存在する
- BOM では、まず pom.xml を持つ BOM プロジェクトを用意する
- BOM プロジェクトの pom.xml には `<dependencyManagement>` が宣言されており、各プロジェクトで使用する依存関係が定義されている
- 各プロジェクトでは、この BOM プロジェクトを `import` スコープで読み込む
- 依存関係のバージョンは BOM プロジェクトで宣言されているので、各プロジェクトは `groupId`, `artifactId` だけを `<dependency>` に宣言すればいい
- これにより、全プロジェクトで使用する依存関係のバージョンを BOM プロジェクトで一元管理できるようになる
- ちなみに、Spring Boot の BOM が [こちら](https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.2.6.RELEASE/spring-boot-dependencies-2.2.6.RELEASE.pom)
    - この BOM を読み込めば、使用するライブラリのバージョンを Spring Boot がサポートしているものに合わせることができる

---

- 各スコープの範囲を俯瞰できるようにするため、スコープとゴールの関連を表にしてみた（`import` は特殊なので除外）

![maven.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/635ed8ee-f378-fe92-1613-50fc3be1ab5e.jpeg)

- `○` が付いているのは、ゴールが該当するスコープの依存関係を参照・使用することを表している
- `war:war` については、生成される war ファイル内に梱包されるかどうか、ということを意味している
- それ以外は、クラスパスに設定されるかどうか、ということを意味している

## 依存関係をツリーで確認する
- プロジェクトの持つ依存関係をツリー構造のグラフで表示して確認できる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>
</project>
```

- Spring Framework の web と jdbc を依存関係に宣言している
- しかし、プロジェクトの依存しているライブラリはこれだけではなく、実際は `spring-web`, `spring-jdbc` が依存するライブラリにも依存している
- 最終的にプロジェクトが依存しているライブラリがいくつあるのかは、 pom.xml を見ただけでは判断できない
- [maven-dependency-plugin](https://maven.apache.org/plugins/maven-dependency-plugin/) の [tree ゴール](https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html) を実行すると、この推移的な依存関係を含めたすべての依存関係をツリー構造のグラフで確認できる

```text:実行結果
> mvn dependency:tree
...
[INFO] +- org.springframework:spring-web:jar:5.2.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.2.5.RELEASE:compile
[INFO] |  \- org.springframework:spring-core:jar:5.2.5.RELEASE:compile
[INFO] |     \- org.springframework:spring-jcl:jar:5.2.5.RELEASE:compile
[INFO] \- org.springframework:spring-jdbc:jar:5.2.5.RELEASE:compile
[INFO]    \- org.springframework:spring-tx:jar:5.2.5.RELEASE:compile
...
```

- `spring-web` が、さらに `spring-beans`, `spring-core` に依存し、さらに `spring-core` が `spring-jcl` に依存していることが分かる
- そして、 `spring-jdbc` は `spring-tx` に依存していることがわかる
    - ちなみに、 `spring-jdbc` は `spring-beans`, `spring-core` にも依存しているが、 `spring-web` 側と重複するため表示は省略されている
- デフォルトでは全てのスコープの依存関係が出力されるが、 [scope パラメータ](https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html#scope) で絞ることもできる

## 依存関係のjarを集める
- プロジェクトが依存する jar ファイルの実物を手元にすべてかき集めたい、となることが稀によくある
- これは、 `maven-dependency-plugin` の [copy-dependencies ゴール](https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html) で実現できる
- ↑の pom.xml と同じ設定で実行してみる

```text:実行結果
> mvn dependency:copy-dependencies
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

> dir /b target\dependency
spring-beans-5.2.5.RELEASE.jar
spring-core-5.2.5.RELEASE.jar
spring-jcl-5.2.5.RELEASE.jar
spring-jdbc-5.2.5.RELEASE.jar
spring-tx-5.2.5.RELEASE.jar
spring-web-5.2.5.RELEASE.jar
```

- `target/dependency` フォルダの下に、依存する jar がすべて出力されている
- デフォルトでは全てのスコープの jar が対象になるが、 [includeScope パラメータ](https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html#includeScope) などで絞り込むことができる

# プロファイル
- 基本的にプロジェクトのビルドは、どの環境でも同じ設定で同じ結果が出力されるようにしておくのがいい
    - ある環境ではビルドは成功するけど、別の環境ではエラーになる、なんてことになると色々面倒
- しかし、実際に開発をしていると、環境に依存したビルド設定が必要になることが稀によくあるらしい（しらんけど）
- ビルド環境によって pom.xml の内容を切り替えたい（特定のプラグインを有効にしたり、設定値を変更したり）場合に、**プロファイル(Profile)**という仕組みが利用できる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <profiles>
    <profile>
      <id>foo</id>

      <properties>
        <message>foo profile!!</message>
      </properties>

      <dependencies>
        <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.10</version>
        </dependency>
      </dependencies>

      <build>
        <directory>${project.basedir}/build</directory>
      </build>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>message = ${message}</echo>
            <echo>project.build.directory = ${project.build.directory}</echo>
            <echo>dependency[0] = ${project.dependencies[0].artifactId}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<profiles><profile>` で、 `foo` というプロファイルを宣言している
- `foo` プロファイルでは、プロパティや依存関係、 `project.build.directory` の設定などを行っている
- `maven-antrun-plugin` を使って、それらの値を表示するようにしている

```text:実行結果
> mvn antrun:run
...
     [echo] message = ${message}
     [echo] project.build.directory = F:\tmp\maven\hello\target
     [echo] dependency[0] = ${project.dependencies[0].artifactId}
...

> mvn -P foo antrun:run
...
     [echo] message = foo profile!!
     [echo] project.build.directory = F:\tmp\maven\hello\build
     [echo] dependency[0] = commons-lang3
```

- 普通に実行した場合は、 `foo` プロファイルで設定した内容は反映されていない
- 一方で、 `-P foo` を指定して実行した場合は、 `foo` プロファイルで設定した内容が反映されているのが分かる
- このように、プロファイルを使用すると、そのプロファイルを使用したときだけ適用される設定を定義できる
- プロファイルの定義は、 `<profile>` で記述する
    - `<id>` で、そのプロファイルを一意に識別するための名前を設定する
    - それ以外は、基本的に pom.xml に記述できる要素（`<dependencies>`, `<build>` など）をそのまま記述できる
- プロファイルの指定は、コマンドラインで `-P` に続けてプロファイルの `<id>` を指定する

## 複数のプロファイルを指定して実行する
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>foo</id>
      <properties>
        <foo>FOO</foo>
        <message>foo profile!!</message>
      </properties>
    </profile>
    <profile>
      <id>bar</id>
      <properties>
        <bar>BAR</bar>
        <message>bar profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <configuration>
          <target>
            <echo>foo = ${foo}</echo>
            <echo>bar = ${bar}</echo>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `foo` と `bar` という２つのプロファイルを宣言している
- `message` は両方のプロファイルで宣言して重複するようにしている

```text:実行結果
> mvn -P bar,foo antrun:run
...
     [echo] foo = FOO
     [echo] bar = BAR
     [echo] message = bar profile!!
```

- プロファイルを複数指定する場合は、カンマ区切りで `id` を並べる
- 重複する要素は、 pom.xml 上で後に来る方が採用されるっぽい（プロファイルの指定順ではなさそう）

## プロファイルをデフォルトで有効にする
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>foo</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <message>foo profile!!</message>
      </properties>
    </profile>
    <profile>
      <id>bar</id>
      <properties>
        <message>bar profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        ...
        <configuration>
          <target>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `foo` プロファイルの方に、 `<activeByDefault>true</activeByDefault>` を設定している

```text:実行結果
> mvn antrun:run
...
     [echo] message = foo profile!!
...

> mvn -P bar antrun:run
...
     [echo] message = bar profile!!
```

- `foo` プロパティがデフォルトで有効になっている
- `<activation><activeByDefault>` に `true` を設定すると、そのプロファイルはデフォルトで有効になる
- `-P` で別のプロファイルが明示された場合は、 `<activeByDefault>` が `true` のプロファイルは無効になる

## 条件が満たされたときだけプロファイルを有効にする
### システムプロパティが宣言されていたら有効にする
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>foo</id>
      <activation>
        <property>
          <name>foo</name>
        </property>
      </activation>
      <properties>
        <message>foo profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        ...
        <configuration>
          <target>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<activation>` の中で、 `<property>` を宣言している

```text:実行結果
> mvn antrun:run
...
     [echo] message = ${message}
...

> mvn -Dfoo antrun:run
...
     [echo] message = foo profile!!
```

- `-P` によるプロファイルの指定はしていないが、 `-Dfoo` でシステムプロパティを宣言することで `foo` プロファイルが有効になっている
- `<activation>` の中では、そのプロファイルを有効にする条件を定義できる
- `<property>` は、システムプロパティが宣言されているか、特定の値が設定されているかを条件に指定できる
- ここでは `<name>foo</name>` だけを設定しているので、システムプロパティ `foo` が設定されていれば、値に関わらずプロファイルが有効になる
- 値も条件にする場合は、次のように `<value>` も宣言する

```xml:システムプロパティの値も条件に入れる場合
        <property>
          <name>foo</name>
          <value>enable</value>
        </property>
```

```text:実行結果
> mvn -Dfoo antrun:run
...
     [echo] message = ${message}
...

> mvn mvn -Dfoo=enable antrun:run
...
     [echo] message = foo profile!!
```

- システムプロパティ `foo` の値が `enable` のときにだけ、 `foo` プロファイルが有効になっている

### JDK のバージョンを条件にする
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>jdk11</id>
      <activation>
        <jdk>11</jdk>
      </activation>
      <properties>
        <message>jdk11 profile!!</message>
      </properties>
    </profile>
    <profile>
      <id>jdk14</id>
      <activation>
        <jdk>14</jdk>
      </activation>
      <properties>
        <message>jdk14 profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        ...
        <configuration>
          <target>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<activation>` で `<jdk>` 要素を使用している

```text:実行結果
> java --version
openjdk 11.0.6 2020-01-14
...

> mvn antrun:run
...
     [echo] message = jdk11 profile!!
...

> java --version
openjdk 14 2020-03-17
...

> mvn antrun:run
...
     [echo] message = jdk14 profile!!
```

- 実行時の JDK のバージョンに合わせてプロファイルが切り替わっている
- `<jdk>` を使用すると、実行時の Java のバージョンをプロファイル適用の条件にできる
- `<jdk>` に記述した条件は前方一致で Java のバージョンと比較される
    - `11` と指定してる場合、前方一致なので `11.0.6` にもマッチする
    - さらに `(,11]` のような範囲指定の記述も可能（この場合は、 11 以下のバージョンにマッチする）
    - 範囲指定の書き方については [Apache Maven Enforcer Built-In Rules – Version Range Specification](https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html) を参照
- なお、比較対象となる Java のバージョンは、システムプロパティ `java.version` で取得された値が使用されている
    - 実装はたぶん [これ](https://github.com/apache/maven/blob/maven-3.6.3/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/JdkVersionProfileActivator.java#L66)
- `<jdk>!11</jdk>` のように、先頭に `!` をつけると条件を否定できる

### OS を条件にする
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>Windows</id>
      <activation>
        <os>
          <name>Windows 10</name>
        </os>
      </activation>
      <properties>
        <message>Windows profile!!</message>
      </properties>
    </profile>
    <profile>
      <id>Linux</id>
      <activation>
        <os>
          <name>Linux</name>
        </os>
      </activation>
      <properties>
        <message>Linux profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        ...
        <configuration>
          <target>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<activation>` で `<os>` を設定している

```text:実行結果(Windows)
> mvn antrun:run
...
     [echo] message = Windows profile!!
```

```text:実行結果(Linux)
> mvn antrun:run
...
     [echo] message = Linux profile!!
```

- 実行時の OS によって、適用されるプロファイルが切り替わっている
- `<os>` を使用すると、実行時に OS をプロファイル適用の条件に使用できる
- `<name>` は、 OS の名前を条件にしている
    - OS の名前はシステムプロパティの `os.name` で取得できる値が使用されている
    - 大文字・小文字の区別はないっぽい
- 名前以外にも、次の条件を指定できる
    - `<family>`
        - OS の種類
        - 指定できる値の一覧は [Apache Maven Enforcer Built-In Rules – Require OS Version](https://maven.apache.org/enforcer/enforcer-rules/requireOS.html) を参照
        - ちなみに、 Linux の場合は `unix` を指定する
    - `<arch>`
        - CPU のアーキテクチャ
        - `x86` とか `amd64` を指定する
    - `<version>`
        - OS のバージョン
- これらの条件を複数指定した場合は、全て AND 条件で結合される
- 今の環境がどういう値になるのかは、 [maven-enforcer-plugin](http://maven.apache.org/enforcer/maven-enforcer-plugin/) の [display-info ゴール](http://maven.apache.org/enforcer/maven-enforcer-plugin/display-info-mojo.html) を実行することで確認できる

```text:実行結果
> mvn enforcer:display-info
...
[INFO] Maven Version: 3.6.3
[INFO] JDK Version: 11.0.6 normalized as: 11.0.6
[INFO] OS Info: Arch: amd64 Family: windows Name: windows 10 Version: 10.0
```

### ファイルの有無を条件にする
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>Exists</id>
      <activation>
        <file>
          <exists>${basedir}/pom.xml</exists>
        </file>
      </activation>
      <properties>
        <message>Exists profile!!</message>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        ...
        <configuration>
          <target>
            <echo>message = ${message}</echo>
          </target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<activation>` で `<file>` を指定している

```text:実行結果
> mvn antrun:run
...
     [echo] message = Exists profile!!
```

- `<file>` を使用すると、ファイルの有無をプロファイル適用の条件に使用できる
- `<exists>` は、そのファイルが存在することを条件に設定する
- ファイルが存在しないことを条件にする場合は、 `<missing>` を使用する
- ファイルパスの指定では、埋め込みパラメータとして `${basedir}` またはシステムプロパティ・リクエストプロパティ？しか使用できないという制限がある
    - `${project.basedir}/pom.xml` とか書いていると、うまく判定されない

## 使用できるプロファイルを確認する
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <profiles>
    <profile>
      <id>foo</id>
      <properties>
        <message>foo profile!!</message>
      </properties>
    </profile>
    <profile>
      <id>bar</id>
      <properties>
        <message>bar profile!!</message>
      </properties>
    </profile>
  </profiles>

  ...
</project>
```

- `foo`, `bar`、２つのプロファイルを定義している

```text:実行結果
> mvn help:all-profiles
...
[INFO] Listing Profiles for Project: example:hello:jar:1.0.0
  Profile Id: bar (Active: false , Source: pom)
  Profile Id: foo (Active: false , Source: pom)
```

- [maven-help-plugin](https://maven.apache.org/plugins/maven-help-plugin/) の [all-profiles](https://maven.apache.org/plugins/maven-help-plugin/all-profiles-mojo.html) を実行すると、使用できるプロファイルを確認できる

## アクティブなプロファイルを確認する
```text:実行結果
> mvn -P foo help:active-profiles antrun:run
...
Active Profiles for Project 'example:hello:jar:1.0.0':

The following profiles are active:

 - foo (source: example:hello:1.0.0)
...
     [echo] message = foo profile!!
```

- [active-profiles](https://maven.apache.org/plugins/maven-help-plugin/active-profiles-mojo.html) を使用すると、実行時にアクティブ（有効）になっているプロファイルを確認できる
- デバッグのときとかに便利かも

## いつプロファイルを使うべきか
- プロファイルを使うと、プロファイルごとに異なるビルド結果を作り出すことができるようになる
- このため、プロファイルを多様しすぎると、開発者によって異なるビルド結果が得られてしまうことになりかねない
    - 「開発用途では `develop` プロファイルを有効にしないといけない」みたいなルールがあって、その指定を忘れていたなど
- ビルド結果が変わるのは、混乱の原因になりかねない
- `<os>` などの条件設定が可能なことから想像できるように、プロファイルの本来の目的は、ビルドを行う環境の差異を吸収することにあると思う
    - したがって、それ以外の用途で用いるのは極力避けたほうが良い気がする（私見）
- 例えば、開発用・検証環境用・本番環境用に使用する設定ファイルを切り替えるような方法では、プロファイルは使うべきではないのかもしれない
    - アプリの設定を環境ごとに切り替える手段は、最近は環境変数を使用するのがベストプラクティスとされている気がする
    - 参考：[III. 設定 | The Twelve-Factor App （日本語訳）](https://12factor.net/ja/config)

# プラグインを自作する
- プラグインは自作できる
- `org.apache.maven.plugins` や `com.codehaus.mojo` にあるプラグインで目的が達成できない場合は、自分でプラグインを作ることになる

## Hello World
```xml:pom.xml
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>example</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0.0</version>
  <packaging>maven-plugin</packaging>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>3.6.3</version>
    </dependency>

    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <version>3.6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-plugin-plugin</artifactId>
        <version>3.6.0</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- packaging を `maven-plugin` にする
- 依存関係として、次の２つを設定する
    - `org.apache.maven:maven-plugin-api`
    - `org.apache.maven.plugin-tools:maven-plugin-annotations`
        - こちらは `provided` スコープで指定している
- `maven-plugin-plugin` を設定している
    - Java 11 でコンパイルしようとすると、このプラグインのバージョンを新しくしておかないとエラーになるっぽい？

```java:HelloMojo.java
package example;

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

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {

    public void execute() throws MojoExecutionException {
        getLog().info("Hello Custom Plugin!");
    }
}
```

- `AbstractMojo` クラスを継承してクラスを作成する
    - この `HelloPlugin` クラスが、１つのゴールに対応する
- `execute()` メソッドの中で、そのプラグインの処理を実装する
- `@Mojo` アノテーションで、ゴールのメタ情報を設定している
    - `name` は、ゴールの名前になる

```text:ビルド
> mvn install
...
```

- `install` フェーズを実行して、作成したプラグインをローカルリポジトリにインストールする
- インストールしたプラグインを、他のプロジェクトで使用してみる

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>

  <build>
    <plugins>
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<plugin>` に `hello-maven-plugin` を設定している

```text:実行結果
> mvn hello:hello
...
[INFO] Hello Custom Plugin!
```

- 自作のプラグインが実行できた

## プラグインの名前（artifactId）
```xml:pom.xml
...
  <groupId>example</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0.0</version>
...
```

- 自作プラグインの `artifactId` は、 `XXX-maven-plugin` とするのが慣例となっている
- このパターンの `artifactId` にしておくと、 `XXX` の部分が自動的にプラグインのプレフィックスとしてメタ定義が作成される
- 試しに、作成された jar ファイルの中を確認する

```text:jarの中身
hello-1.0.0.jar/
 |-example/
 `-META-INF/
   |-MANIFEST.MF
   `-maven/
     |-example/
     `-plugin.xml
```

- この `plugin.xml` の中身を見ると、次のようになっている

```xml:plugin.xml
...
<plugin>
  <name>hello-maven-plugin</name>
  <description></description>
  <groupId>example</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0.0</version>
  <goalPrefix>hello</goalPrefix>
  <isolatedRealm>false</isolatedRealm>
  <inheritedByDefault>true</inheritedByDefault>
...
```

- `<goalPrefix>` が `hello` になっている
- これによって、このプラグインは `hello:<ゴール>` というプレフィックス指定ができるようになっている
- もし `foo-maven-plugin` という `artifactId` にしていれば、プレフィックスは `foo` になっている

## Mojo
- Maven のプラグインのゴールを実装するクラスのことを、**Mojo(Maven Old Java Object)**と呼ぶ
- Maven のドキュメントを読んでいると、ところどころでこの Mojo という用語が現れる
- その場合は、ゴールを実装したクラスのこと、と思えばいいと思う

## パラメータを定義する
```java:HelloMojo.java
package example;

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

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {
    @Parameter
    private String message;

    public void execute() throws MojoExecutionException {
        getLog().info("message = " + message);
    }
}
```

- `message` フィールドを Mojo に追加して、 `@Parameter` アノテーションを付けている
- Getter, Setter は定義していない

```xml:pom.xml(別プロジェクト)
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
        <configuration>
          <message>Hello World!!</message>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<configuration>` で、 Mojo のフィールド名と同じ要素を宣言している

```text:実行結果
> mvn hello:hello
...
[INFO] message = Hello World!!
```

- Mojo で宣言した `message` フィールドを、パラメータとして指定できるようになっている
- ちなみに、この状態だとシステムプロパティで値を指定することはできない

## システムプロパティでパラメータを指定できるようにする
```java:HelloMojo.java
package example;
...

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {
    @Parameter(property="hello.message")
    private String message;

    public void execute() throws MojoExecutionException {
        getLog().info("message = " + message);
    }
}
```

- `@Parameter` の `property` に、システムプロパティで指定するときの名前を設定する

```xml:pom.xml（別プロジェクト）
...
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
      </plugin>
...
```

- `<configuration>` の指定はしないようにする（設定していると、こちらが優先される）

```text:実行結果
> mvn hello:hello -Dhello.message="HELLO WORLD!!"
...
[INFO] message = HELLO WORLD!!
```

- システムプロパティ経由で値を設定できるようになった

## パラメータのデフォルト値を設定する
```java:HelloMojo.java
package example;
...

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {
    @Parameter(property="hello.message", defaultValue="Hello Custom Plugin!!")
    private String message;

    public void execute() throws MojoExecutionException {
        getLog().info("message = " + message);
    }
}
```

- `@Parameter` アノテーションの `defaultValue` で、そのパラメータのデフォルト値を宣言できる

```xml:pom.xml(別プロジェクト)
...
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
      </plugin>
...
```

- `<configuration>` は未設定にしている

```text:実行結果
> mvn hello:hello
...
[INFO] message = Hello Custom Plugin!!
...

> mvn hello:hello -Dhello.message=OVERRIDE!!
...
[INFO] message = OVERRIDE!!
```

- 何も設定されていない場合は、 `defaultValue` で設定した値が採用されているのがわかる

### デフォルト値に式を使用する
```java:HelloMojo.java
package example;
...

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {
    @Parameter(defaultValue="${hello.mojo.message}")
    private String message;

    public void execute() throws MojoExecutionException {
        getLog().info("message = " + message);
    }
}
```

- `defaultValue` の値には、 `${...}` のようにして式を記述できる
- 式の中で参照できる値については [PluginParameterExpressionEvaluator の Javadoc](https://maven.apache.org/ref/current/maven-core/apidocs/org/apache/maven/plugin/PluginParameterExpressionEvaluator.html) を参照
- システムプロパティやプロジェクトのプロパティ（`<properties>`）も参照できる

```xml:pom.xml(別プロジェクト)
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <properties>
    <hello.mojo.message>Project Property</hello.mojo.message>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
      </plugin>
    </plugins>  
  </build>
</project>
```

- `<properties>` で `defaultValue` の式で宣言されていたものと同じキーで値を宣言している

```text:実行結果
> mvn hello:hello
...
[INFO] message = Project Property
...

> mvn hello:hello -Dhello.mojo.message="System Property"
...
[INFO] message = System Property
```

- `<properties>` で宣言された値が `messge` パラメータに設定されている
- システムプロパティで上書きすることもできている

## パラメータに使用できる型
- パラメータの型は、 `String` だけでなく様々な型で宣言できる

```java:HelloMojo.java
package example;

import java.io.File;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;

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

@Mojo(name="hello")
public class HelloMojo extends AbstractMojo {
    @Parameter
    private int intValue;
    @Parameter
    private long longValue;
    @Parameter
    private boolean booleanValue;
    @Parameter
    private double doubleValue;
    @Parameter
    private Date dateValue;
    @Parameter
    private File fileValue;
    @Parameter
    private URL urlValue;
    @Parameter
    private HelloEnum enumValue;
    @Parameter
    private List<String> listValues;
    @Parameter
    private Map<String, String> mapValue;

    public void execute() throws MojoExecutionException {
        Log log = getLog();
        log.info("intValue=" + intValue);
        log.info("longValue=" + longValue);
        log.info("booleanValue=" + booleanValue);
        log.info("doubleValue=" + doubleValue);
        log.info("dateValue=" + dateValue);
        log.info("fileValue=" + fileValue);
        log.info("urlValue=" + urlValue);
        log.info("enumValue=" + enumValue);
        log.info("listValues=" + listValues);
        log.info("mapValue=" + mapValue);
    }

    public enum HelloEnum {
        HELLO,
        WORLD;
    }
}
```

- 様々な型でパラメータを定義している

```xml:pom.xml(別プロジェクト)
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
        <configuration>
          <intValue>123</intValue>
          <longValue>1234567890</longValue>
          <booleanValue>true</booleanValue>
          <doubleValue>1234.5678</doubleValue>
          <dateValue>2019-10-20 12:13:14</dateValue>
          <fileValue>foo/bar</fileValue>
          <urlValue>https://www.google.co.jp/</urlValue>
          <enumValue>HELLO</enumValue>
          <listValues>
            <aaa>fizz</aaa>
            <bbb>buzz</bbb>
          </listValues>
          <mapValue>
            <foo>FOO</foo>
            <bar>BAR</bar>
          </mapValue>
        </configuration>
      </plugin>
    </plugins>  
  </build>
</project>
```

```text:実行結果
> mvn hello:hello
...
[INFO] intValue=123
[INFO] longValue=1234567890
[INFO] booleanValue=true
[INFO] doubleValue=1234.5678
[INFO] dateValue=Sun Oct 20 12:13:14 JST 2019
[INFO] fileValue=F:\tmp\maven\hello\foo\bar
[INFO] urlValue=https://www.google.co.jp/
[INFO] enumValue=HELLO
[INFO] listValues=[fizz, buzz]
[INFO] mapValue={bar=BAR, foo=FOO}
...
```

- `int` や `long`, `float`, `double`, `boolean` などのプリミティブ型は、普通に使用可能
    - ラッパークラスも可能
- `java.util.Date` は、次のいずれかの書式で指定する
    - `yyyy-MM-dd HH:mm:ss.S a` （例：`2005-10-06 2:22:55.1 PM`）
    - `yyyy-MM-dd HH:mm:ssa` （例：`2005-10-06 2:22:55PM`）
    - ただし、完全に一致していなくても、そこそこ柔軟に解析してくれる（AM, PM は省略できるけど、時刻は省略できないっぽい）
- `java.io.File` は、値をパスとして扱う
- `java.net.URL` は、値を URL として扱う
- enum 型の場合は、列挙された定数と同じ値を指定することで設定可能
- `List` の場合は、パラメータ名と同じ要素を書いた中に任意の名前の要素を列挙すれば、それが `List` の要素として処理されるっぽい
    - 普通は `<listValues><listValue>...` のように記述するのがわかりやすいと思う
- `Map` の場合は、入れ子要素の名前がキーに、要素の値がバリューになる
    - 検証は省略したけど、 `java.util.Properties` も同じように使用できる

## デフォルトのフェーズを紐付ける
```java:HelloMojo.java
package example;

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;

@Mojo(name="hello", defaultPhase=LifecyclePhase.VALIDATE)
public class HelloMojo extends AbstractMojo {

    public void execute() throws MojoExecutionException {
        getLog().info("Hello Mojo!!");
    }
}
```

- `@Mojo` の `defaultPhase` で、デフォルトのフェーズを指定できる
- フェーズの指定には、 `LifecyclePhase` 列挙型を使用する
- ここでは `validate` フェーズに紐づけている

```xml:pom.xml(別プロジェクト)
...
      <plugin>
        <groupId>example</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0.0</version>
        <executions>
          <execution>
            <goals>
              <goal>hello</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
...
```

- `hello` ゴールだけ宣言して、フェーズの紐付けは行っていない

```text:実行結果(別プロジェクト)
> mvn validate
...
[INFO] Hello Mojo!!
```

- `validate` フェーズの実行で `hello` ゴールが実行されている

# 生成物を削除する
```text:フォルダ構成
|-pom.xml
`-target/
```

```xml: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>example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0.0</version>
</project>
```

```text:実行結果
> mvn clean
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

```text:フォルダ構成（実行後）
`-pom.xml
```

- [maven-clean-plugin](https://maven.apache.org/plugins/maven-clean-plugin/) の [clean ゴール](https://maven.apache.org/plugins/maven-clean-plugin/clean-mojo.html) を実行すると、プロジェクトの生成物をすべて削除できる
    - ここで指定しているのは `clean` フェーズ
    - `clean` ゴールは、デフォルトで `clean` フェーズに紐付いている（ややこしい）
- デフォルトでは、次のフォルダが削除対象となっている
    - `${project.build.directory}`
    - `${project.build.outputDirectory}`
    - `${project.build.testOutputDirectory}`
    - `${project.reporting.outputDirectory}`
- 特に上記フォルダのパスを変更していないのであれば、 `${project.basedir}/target` が削除される

## 削除対象のフォルダを追加する
```text:フォルダ構成
|-pom.xml
|-target/
`-foo/
```

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <filesets>
            <fileset>
              <directory>${project.basedir}/foo</directory>
            </fileset>
          </filesets>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

- `<filesets>` を追加して、 `foo` フォルダも削除対象に追加している

```text:実行結果
> mvn clean
...
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ hello ---
[INFO] Deleting ...\foo (includes = [], excludes = [])
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

```text:フォルダ構成（実行後）
`-pom.xml
```

- `target` だけでなく、 `foo` も削除されている
- [filesets パラメータ](https://maven.apache.org/plugins/maven-clean-plugin/clean-mojo.html#filesets) で、削除対象のフォルダやファイルを追加できる
- `<filesets>` は、 [Fileset](https://maven.apache.org/plugins/maven-clean-plugin/apidocs/org/apache/maven/plugins/clean/Fileset.html) のコレクションを指定する
    - `<directory>` だけでなく、 `<includes>` や `<excludes>` でファイルを絞ることもできるっぽい（試してない）


# 参考兼公式ドキュメント目次
<del>どこになんの情報があるのか分かりにくすぎるので整理してみる</del>

- POM
    - [Maven – POM Reference](https://maven.apache.org/pom.html)
    - [Maven – Introduction to the POM](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html)
    - [Maven Model Builder – Super POM](https://maven.apache.org/ref/current/maven-model-builder/super-pom.html)
    - [Apache Maven Archiver – Reference](http://maven.apache.org/shared/maven-archiver/index.html)
- プラグイン
    - [Maven – Available Plugins](https://maven.apache.org/plugins/index.html)
    - [Maven – Guide to Configuring Plug-ins](https://maven.apache.org/guides/mini/guide-configuring-plugins.html)
    - [Maven – Guide to Developing Java Plugins](https://maven.apache.org/guides/plugin/guide-java-plugin-development.html)
    - [Mojo – Plugins](https://www.mojohaus.org/plugins.html)
- ビルドライフサイクル
    - [Maven – Introduction to the Build Lifecycle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
    - [Lifecycle Reference](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference)
    - [Maven Core – Plugin Bindings for Default Lifecycle Reference](https://maven.apache.org/ref/current/maven-core/default-bindings.html)
- プロファイル
    - [Maven – Introduction to build profiles](https://maven.apache.org/guides/introduction/introduction-to-profiles.html)
- 依存関係の解決
    - [Maven – Introduction to the Dependency Mechanism](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)
- フォルダ構成
    - [Maven – Introduction to the Standard Directory Layout](https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html)
- Javadoc
    - [Maven Model API](https://maven.apache.org/ref/current/maven-model/apidocs/)