プロジェクトの影の働き者「Maven」と仲良くなりたい！

# 背景

仕事で「動的WebシステムはちゃんとMaven化してね！」と言われました。
Mavenのことについて何も知らず「なんのこっちゃ」な感じだったので「Mavenとは何か？」「Mavenを使うと何ができるのか？」について調べてみました。

# Mavenとは何か？

「ソフトウェアの開発プロジェクト」をモデル化したものをPOM(**P**roject **O**bject **M**odel)と言います。Mavenとセットで現れるpom.xmlの名前の由来もこれです。Mavenは **開発プロジェクト自体を大きな一つのオブジェクトとして捉えて、それに対して様々な働きかけをしてくれます。**

具体的にはこんなふうに役立ってくれます。

1. 必要なライブラリは自分でダウンロードし、フォルダに配置しなければいけない...。<br>
　→pomに必要な記述をすることで自動的に配置してくれる！
2. いちいち毎回テストするのが面倒臭い...。<br>
　→デプロイやインストールなどの処理と同時に自動でテストを回してくれる！
3. 作成者や導入されているプラグインなど、プロジェクトの基本的な情報が分からない...。<br>
　→基本的な情報をまとめたhtmlを作成してくれる！
4. 簡単にプロジェクトのjar/warファイルを作成したい...。<br>
　→コマンド一つで作成してくれる！

# Mavenを使うと何ができるのか？

と言うわけで、実際に動かしてみました。動的Webプロジェクトに対し、Mavenを適応していきます。

## 動作環境

* 端末：MacbookAir
* OSバージョン：macOS Catalina バージョン 10.15.4
* eclipse Version: 2020-03 (4.15.0)

## Maven化動的Webプロジェクト作成手順

1. eclipseを起動します
2. ファイル→新規→動的Webプロジェクトを選択します
3. 新規動的Webプロジェクト作成画面になるので、プロジェクト名を設定して「完了」を押します
4. 動的Webプロジェクトが完成するので、プロジェクト名を右クリックします
5. 構成→Mavenプロジェクトに変換を選択します
6. Mavenプロジェクト作成画面になるので、そのまま「完了」を押します
7. Maven化されたプロジェクトができます！プロジェクトのアイコン左上に「M」が付いたらOKです！

実際に動かしてみる前に「Mavenがどんなふうに実行されるのか？」を調べてみました。 **Mavenが仕事をするには、まず各仕事に対応したプラグインを導入します。そして導入したプラグインが各々の中に定義された処理を分担して実行しています。** 理屈はいいので何ができるのかだけ知りたいよと言う方は[こちらへ](#~~やっと~~実際にやってみた)。

## pom.xmlには何が書いてるの？

先ほど作成したプロジェクトに、pom.xmlと言うファイルがあるので、中を見てみましょう。「実効POM」なるタブを見つけたのでそこを押すと、長ーーーいxml記述を見つけました！ここに処理の詳細が書かれている様子。全て書くのは死にそうになるので、ざっくりした構成を書いてみました。

```xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  〜バージョンなど、このプロジェクトの基本的な情報〜

  <repositories>
    <repository>
      〜ライブラリを取得する時に参照するリポジトリの情報〜
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      〜プラグインを取得する時に参照するリポジトリの情報〜
    </pluginRepository>
  </pluginRepositories>

  <build>
    〜ビルド時に実行する処理の詳細〜
  </build>

  <reporting>
    〜レポート作成時に実行する処理の詳細
  </reporting>

</project>
```

まずはこのような区分けができると思いました。<br>今回は **〜ビルド時に実行する処理〜** にスポットを当てるため、buildタグの中をさらに詳しくみていきます。

```xml
<build>

〜ビルド時に作成したファイルの置き場所などの情報〜

    <resources>
      <resource>
        〜プロジェクトリソースの場所〜
      </resource>
    </resources>

    <testResources>
      <testResource>
        〜テストリソースの場所〜
      </testResource>
    </testResources>

    〜SNAPSHOTを格納するフォルダ、ファイル名〜

    <pluginManagement>
    　〜子のpomに継承するプラグイン一覧〜
      <plugins>
        <plugin>
          〜プラグイン〜
        </plugin>
        ...
      </plugins>
    </pluginManagement>

    <plugins>
    　〜このプロジェクトで扱うプラグイン一覧〜
      <plugin>
        〜プラグイン〜
      </plugin>
      ...
    </plugins>

</build>
```
上でも書いたように **Mavenが仕事をするには、各仕事に対応したプラグインを導入する必要があります。** それらを指定しているのがpluginタグです。つまり、ここに表記のあるプラグインに対応した仕事を、Mavenで実行させることができます。では実際にそれぞれのプラグインがどんな仕事をしてくれるのかについて、pluginタグの中身を見つつ調べてみることにします。

## ゴールって何？

pluginタグはたくさんあるのですが、その中の一つをピックアップして見てみます。

```xml
<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.12.4</version>
    <executions>
        <execution>
        <id>default-test</id>
        <phase>test</phase>
        <goals>
            <goal>test</goal>
        </goals>
        </execution>
    </executions>
</plugin>
```
この「maven-surefire-plugin」はMavenでテストを行うためのプラグインです。注目して欲しいのが、 **phase**タグ、 **goal**タグ。これらが仕事の命令を示すタグになります。例えばこのプラグインが入った状態で「test」をゴールに設定してビルドを実行すると、このpluginがtest処理をこなしてくれます。

ただし、test処理だけが実行されるわけではありません。各処理の間には決められた順番があり、testが行われる前に実行されるべき処理を全て通ってからtestが実行されることになります。この順番が **(ビルド)ライフサイクル**と呼ばれるものです。

## ライフサイクルって何？

mavenには主に以下３つのライフサイクルがあります。

* clean : Mavenがビルド時に作った成果物を削除する
* default : プロジェクトのデプロイまでを行う。処理の順番はpackageにより異なる
* site : サイトドキュメントを作成する

defaultライフサイクルでの順番はpom.xmlの上の方にあるpackageタグの値によって決まります。[ライフサイクルのリファレンス](https://maven.apache.org/ref/3.6.0/maven-core/default-bindings.html)を見ると値と処理順の関連がわかります。自分の場合はpackageタグの値が「war」だったので、以下の順番が適応されることになります。

```xml
<phases>
  <process-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:resources
  </process-resources>
  <compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
  </compile>
  <process-test-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
  </process-test-resources>
  <test-compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
  </test-compile>
  <test>
    org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
  </test>
  <package>
    org.apache.maven.plugins:maven-war-plugin:2.2:war
  </package>
  <install>
    org.apache.maven.plugins:maven-install-plugin:2.4:install
  </install>
  <deploy>
    org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
  </deploy>
</phases>
```

なので、例にあったように「test」をゴールとしてMavenに命令を下すと、 **test処理だけが実行されるわけでなく、resources→compile→testResources→testCompile→testと言う一連の処理が実行されることになります。** 前の処理でエラーが発生すると、test処理までたどり着くことなくビルドは失敗します。

## ~~やっと~~実際にやってみた

では実際に色んな処理をMavenにやってもらいましょう。

1. [テスト](#テスト)
2. [インストール](#インストール)
3. [デプロイ](#デプロイ)
4. [クリーン](#クリーン)
5. [サイト](#サイト)

### テスト

例にあげたのでまずこちらから。testでは **自分がJUnitなどで定義したUnitTestを一通り行ってくれます。** eclipse上での手順は

* プロジェクトを右クリック→実行→「Maven test」を押す
* 終わり

たったこれだけ！なんて簡単！実行時のコンソールを見てみましょう

```
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< maven-test:maven-test >------------------------
[INFO] Building maven-test 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-test ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-test ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-test ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ maven-test ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven-test ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.554 s
[INFO] Finished at: 2020-05-18T16:11:48+09:00
[INFO] ------------------------------------------------------------------------

```
よくよくみてみると、ライフサイクルの順番にしたがって「resources→compile→testResources→testCompile→test」と処理が実行されているのがわかります。今回はJUnitのテストを作成してないので何もテストを実行せず終了になっていますが、テストを書いていると、`[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven-test ---`以下にこんな感じで続きます。

```
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ Ichirin ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running Login.LoginServletTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.462 s - in Login.LoginServletTest
[INFO] Running com.UserDAOTest
[WARNING] Tests run: 2, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 0 s - in com.UserDAOTest
[INFO] 
[INFO] Results:
[INFO] 
[WARNING] Tests run: 3, Failures: 0, Errors: 0, Skipped: 2
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.884 s
[INFO] Finished at: 2020-05-18T20:24:02+09:00
[INFO] ------------------------------------------------------------------------
```
「３つのテストがあって１つは合格し、２つはスキップされました」と言うことを表しています。ここでFailure, Errorになるテストがあれば、テストビルドは失敗します。

### インストール

インストールでは、 **pom.xmlのdependencyタグで定義したライブラリを、プロジェクトの中で使えるようにしてくれます。** 普通なら自分でダウンロードして、フォルダに配置するまでしないといけないのですが、その辺の手続きをMavenで請け負ってくれます。今回は試しに「Mockito」と言うテスト用モックを使用できるライブラリを入れてみました。

* [Mavenリポジトリ](https://mvnrepository.com/)にアクセスして「Mockito」を検索
* Centralタブでバージョンを選択
* Mavenタブの中にxml文があるので、コピー
* pom.xmlのbuildタグの直前にdependenciesタグを追加
* dependenciesタグの中に先ほどコピーした内容をペースト
* プロジェクトを右クリック→実行→「Maven install」を押す

するとコンソールはこんなふうに。

```
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< maven-test:maven-test >------------------------
[INFO] Building maven-test 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-test ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-test ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-test ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ maven-test ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven-test ---
[INFO]
[INFO] --- maven-war-plugin:3.2.3:war (default-war) @ maven-test ---
[INFO] Packaging webapp
[INFO] Assembling webapp [maven-test] in [/Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/target/maven-test-0.0.1-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/WebContent]
[INFO] Webapp assembled in [29 msecs]
[INFO] Building war: /Users/higuchitakayuki/Dropbox/forDevelop/eclipseProject/maven-test/target/maven-test-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.919 s
[INFO] Finished at: 2020-05-18T16:45:12+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:3.2.3:war (default-war) on project maven-test: Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode) -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

```
どうやら途中でエラーになったようです。でも、ビルドの順番やライフサイクルについて知っていれば、コンソールからどこでコケているのかわかるはず...! 今回はどうやらdefaultライフサイクルの「test」までは無事実行でき、次の「war」のゴールまでたどり着けずにビルドが失敗したようです。[ERROR]の始まった部分を読んでみると、どうやら「web.xmlがないよ！」と怒られたみたいです。ここの文章をコピーして調べると、[こちらの記事](https://www.codeflow.site/ja/article/maven__maven-webxml-attribute-is-required)を発見。どうやらpom.xmlに新たなタグを入れる必要があるようなので、新たに以下の手順を追加します

* 以下のようにpom.xmlのmaven-war-pluginプラグインタグを変更する

```xml
<plugin>
	<artifactId>maven-war-plugin</artifactId>
	<version>3.2.3</version>
	<configuration>
		<warSourceDirectory>WebContent</warSourceDirectory>
        <failOnMissingWebXml>false</failOnMissingWebXml>　<!-- この１行を追加！！ -->
	</configuration>
</plugin>
```

変更後、改めて「Maven install」を実行してみると、きちんとビルドが成功し、ライブラリの中に「Maven依存関係」なるものが出現。その下にお目当てのMockitoファイルが入りました！pomにコピペをするだけでライブラリを持ってこれるのはとても楽ですね！！

また、ここで注目して欲しいのは、インストール処理の前に、テスト処理があること。この後のデプロイにも言えますが、 **defaultライフサイクルにおいてtestよりあとの処理を要求される度に、自動でテスト処理が実行されます！** 自分でテストをしようとしなくても、ことあるごとに勝手にテストが実行されていくわけですね！

### デプロイ

デプロイを行うと、 **プロジェクトの全ファイルを一つにまとめたwar/jarファイルを生成することができます。** このファイルをやりとりすることでプロジェクトを他者と共有できるようになります。

ここでもビルドエラーがあり原因調査があったりしたのですが、長くなるので割愛します。大体インストールの時と同じように原因を探り、[こちらの記事](https://www.techscore.com/tech/Java/ApacheJakarta/Maven/2-4/)を参考にpom.xmlを書き換え、無事ビルドを通しました。手順としては以下のようになりました。

* pom.xmlに以下の記述を追加する

```xml
<project>
    ...
	<distributionManagement>
		<repository>
			<id>deploy-repository</id>
			<name>deployRepository</name>
			<url>file://[warファイルを配置したい場所のパス]</url>
		</repository>
	</distributionManagement>
</project>
```
* プロジェクトを右クリック→実行→「Maven ビルド」を押す
* ゴールを入力する画面が現れるので「deploy」と入力し、「実行」を押す
* 手順1.でurl指定した場所にwarなど各種ファイルが完成している

distributionManagementタグで「成果物をどこに置くか？」を設定する必要があったみたいです。このwarファイルを用いてプロジェクトのやりとりを行うことができます！

### クリーン

クリーンすることで、 **これまでのビルドの成果物を全削除することができます** 。リセットボタンのような物ですね。ビルド時によくわからないエラーが出た時などにこれをやると、悪さをしているデータも一緒に消してくれ、再度ビルドをかけるとエラーが直っていたりします。手順はとても簡単。

* プロジェクトを右クリック→実行→「Maven clean」を押す
* 終わり

この処理はcleanライフサイクルとして独立しているので、testやcompileといった処理は行われません。cleanが完了すると、targetフォルダの中身が消えているのが確認できるかと思います。インストールやらデプロイやら様々なビルドの成果物はここに格納されているのですね。

### サイト

サイトを実行すると、 **プロジェクトの概要をまとめた物を表示したhtmlを作成してくれます。** プロジェクトの概要を知らせる時に便利になりそうですね。手順は以下の通りです。

* pom.xmlのpluginsタグの中に、以下のようにプラグインを2つ追加する

```xml
<plugins>
    ...
    <plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-site-plugin</artifactId>
	    <version>3.7.1</version>
    </plugin>
    <plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-project-info-reports-plugin</artifactId>
	    <version>3.0.0</version>
    </plugin>
</plugins>
```
* プロジェクトを右クリック→実行→「Maven ビルド」を押す
* ゴールを入力する画面が現れるので「site」と入力し、「実行」を押す

siteについてもclean同様ライフサイクルが独立しています。これを実行すると、本物のtargetフォルダの配下に(eclipse上には現れません)、siteフォルダができており、その下にhtmlファイルが複数生成されています。これを実行すると、以下のような感じにページが表示されます。内容についてだったりカスタマイズできるのかについては追々調べたいと思います！

<img width="1212" alt="site.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627131/612976e3-acb1-57b2-e5d8-bfbc5485d0c6.png">

# まとめ

以上、Mavenビルドについてについてまとめてみました。タイトルの通りMavenは**プロジェクトを様々な角度から支えてくれるツールであるようです。**pomを使いこなせばさらに色んなことができそうな予感！

また、ここでは触れませんでしたが、弊社では「親POM」を全社の基盤として持っておいて、それを継承するpomを使ってシステムを作ることで、各人が勝手に色んなライブラリを参照しないような制御をかけたりしているみたいです。先輩からMaven化の指示が出たのはこれと、テストの自動化が理由かと思われます。

初めてqiitaに投稿してみたのですが、思ったよりも長くなり、時間もかかりました...。ただその分理解は深まったように思います。何か内容に不備、間違いなどございましたらご指摘くださると幸いです。

# 参考

* Maven連載-第一回 Mavenって何？
[http://objectclub.jp/technicaldoc/tools/maven/maven01](http://objectclub.jp/technicaldoc/tools/maven/maven01)

* Mavenのビルドライフサイクル
[https://qiita.com/kawakawaryuryu/items/96db58fb9a607973eca0](https://qiita.com/kawakawaryuryu/items/96db58fb9a607973eca0)

* EclipseでMavenを使ってライブラリを利用する方法
[https://reasonable-code.com/eclipse-maven/](https://reasonable-code.com/eclipse-maven/)