今さら聞けないMaven – 3.9.0で追加されたローカルリポジトリの分割

| 6 min read
Author: toshio-ogiwara toshio-ogiwaraの画像

これまで久しく大きな機能追加が行われてこなかったMavenですが、2023年1月31日にリリースされたMaven 3.9.0でコレは!と思うローカルリポジトリの分割機能が追加されました。今回はこのローカルリポジトリの分割機能を紹介します。

なお、3.9.0ではJava8が必須になるなど他に多数の改善や変更が加えられています。他の変更詳細についてはリリースノートを参照ください。

ローカルリポジトリの分割機能の概要

#

Mavenはリモートリポジトリから取得したアーティファクトやmvn installコマンドでローカルインストールしたアーティファクトを.m2ディレクトリなどのローカルリポジトリに保存します。

しかし、このローカルリポジトリにはリモートやローカルといった取得元の区別やreleaseバージョンかそれともsnapshotバージョンか?などの区別はなく、すべてのアーティファクトが一緒くたに保存されます。このため、以下のような問題がありました。

  • (ビルドの調子が悪い時など)ローカルインストールしたものやsnapshotバージョンを削除したい場合があるが、まとめて削除することができない。
  • Gitのブランチ戦略により1つのマイルストーンに対し複数ブランチを作ることが一般的になっているが、ローカルリポジトリは1つのためブランチ間で意図しないアーティファクトの参照や上書きといったことが起こる。

これらの問題を解決するために3.9.0で導入されたのがローカルリポジトリの分割機能です。この機能が導入されたことでこれまで一緒くただったローカルリポジトリの内部をリモートから取得したものとローカルインストールしたもの、さらにreleaseバージョンとsnapshotバージョンとでアーティファクトを区別して管理できるようになりました。

また、これに加えて、リモートとローカルの取得元別にアーティファクトを格納するディレクトリを指定できるようになっているため、異なるディレクトリを指定することでローカルリポジトリを実質的に複数持てるようになりました。

概要の説明は以上にして、ここからは実際の使い方をみていきます。

Mavenのインストール

#

Mavenのダウンロードページから3.9.0(もしくはそれ以降のバージョン)をダウンロードしてローカルに展開します。Mavenの細かいインストール方法は説明しませんが-versionオプションで3.9.0が使われることを確認します。

> mvn -version
Apache Maven 3.9.0 (....)
~ 省略 ~

3.9.0未満のMavenでローカルリポジトリの分割機能を使ってもエラーにならず正常に動作するため、間違いに気がつきにくいです。このためmvnコマンドで3.9.0が使われていることを必ず確認してください。

リモートとローカルの分割

#

リモートとローカルの取得元別にアーティファクトが格納されるようにしてみましょう。

分割前(これまでの動作)

#

分割機能を使わずローカルインストールした場合のローカルリポジトリの内容を確認するため、次のアーティファクトをローカルインストールしてみます。

<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
<modelVersion>4.0.0</modelVersion>
<groupId>sample</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Hello World Sample</name>
</project>

このアーティファクトは"hello world"をコンソールに出力する簡単なアプリで、アーティファクトのグループIDはsample、アーティファクトIDはhello-world、バージョンは0.0.1-SNAPSHOTとしています。

このpomを次のコマンドでローカルインストールします。

mvn install
ローカルリポジトリの一時的な変更

ローカルリポジトリのデフォルトは<ユーザのホームディレクトリ>/.m2/repositoryですが、お試しで通常使っているリポジトリを汚したくない場合は次のように-Dmaven.repo.localオプションを指定して一時的に切り替えることができます。

mvn -Dmaven.repo.local=./temp-repo install

上記実行後のローカルリポジトリの中身は次のようになっています。

./temp-repo
|-- aopalliance
| `-- aopalliance
|-- com
| |-- google
| `
-- thoughtworks
|-- ...
`-- sample
`
-- hello-world

ローカルインストールしたsample:hello-worldもリモートリポジトリから取得したアーティファクト[1]と区別なく同じディレクトリに格納されます。

リモートとローカルを分割してみる

#

同じpomを今度は3.9.0から導入された分割機能を使ってリモートとローカルの取得元別にアーティファクトが格納されるようにしてみます。この指定は-Daether.enhancedLocalRepository.splitオプション(値なし)を使って次のように行います。

mvn -Daether.enhancedLocalRepository.split install

ローカルリポジトリを確認すると次のとおりになっています。なお、変化が分かりやすいように取得済みのアーティファクトはすべて削除してからコマンドを実行しています。これは以降の手順も同じとなります。

./temp-repo/
|-- cached # ←リモートリポジトリのキャッシュ
| |-- aopalliance
| | `-- aopalliance
| |-- com
| | |-- google
| | `
-- thoughtworks
| |-- ...
|
`-- installed # ←ローカルインストールしたもの
`
-- sample
`-- hello-world

リポジトリ直下がcachedディレクトとinstalledディレクトリの2つに分けられ、cachedディレクトにはリモートリポジトリから取得したアーティファクトが、そしてinstalledディレクトリにはローカルインストールしたsample:hello-worldアーティファクトが格納されるようになります。

releaseとsnapshotバージョンの分割

#

今度は上記に加え、さらにreleaseバージョンとsnapshotバージョンが別になるようにしてみます。これには先ほどの指定に-Daether.enhancedLocalRepository.splitLocal-Daether.enhancedLocalRepository.splitRemoteオプション(値なし)を追加して次のように実行します。

mvn -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.splitLocal \
-Daether.enhancedLocalRepository.splitRemote \
install

ローカルリポジトリを確認すると次のとおりになっています。

./temp-repo/
|-- cached
| `-- releases # ←リリースバージョンのフォルダ
| |-- aopalliance
| | `
-- aopalliance
| |-- com
| | |-- google
| | `-- thoughtworks
| |-- ...
|
`
-- installed
|-- releases # ←リリースバージョンのフォルダ
| `-- sample
| `
-- hello-world
| `-- maven-metadata-local.xml
`
-- snapshots # ←snapshotバージョンのフォルダ
`-- sample
`
-- hello-world
`-- 0.0.1-SNAPSHOT

今度はcachedディレクトリの下にreleasesディレクトリが、そしてinstalledディレクトリの下にはreleasessnapshotsディレクトリが作られ、snapshotバージョンのsample:hello-worldアーティファクトがsnapshotsディレクトリに格納されるようになります。

今回はリモートリポジトリから取得したアーティファクトもreleaseとsnapshotバージョンで分けるようにしたため、-Daether.enhancedLocalRepository.splitRemoteを指定しましたが、リモートリポジトリ側の分割が不要な場合はこの指定を省略します。

ローカルインストールディレクトリの指定

#

ローカルインストールしたアーティファクトのディレクトリ名はこれまでinstalledでしたが、これは-Daether.enhancedLocalRepository.localPrefixオプションで変えることができます。

このオプションを指定することでブランチごとにローカルリポジトリ内のローカルインストールディレクトリを変えることができるようになります。例えば、feature/maven-split-local-repositoryブランチのローカルインストールディレクトリを作る場合は次のようになります。

mvn -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.localPrefix=feature/maven-split-local-repository \
install

ローカルリポジトリを確認すると次のとおりになっています。

./temp-repo/
|-- cached
| |-- aopalliance
| | `-- aopalliance
| |-- com
| | |-- google
| | `
-- thoughtworks
| |-- ...
|
`-- feature # ←ブランチ名のディレクトリ(1)
`
-- maven-split-local-repository # ←ブランチ名のディレクトリ(2)
`-- sample
`
-- hello-world
|-- 0.0.1-SNAPSHOT
`-- maven-metadata-local.xml

これまでinstalledディレクトリだったローカルインストールディレクトリがfeature/maven-split-local-repositoryになります。このようにすることでブランチごとにローカルインストールディレクトリを独立して保持できるようになります。

なお、上記はローカルインストール側のディレクトリを指定する例でしたが、必要な場合はリモートリポジトリ側のディレクトリ名も-Daether.enhancedLocalRepository.remotePrefixオプションでcachedから変更することもできます。

最後に

#

Mavenのリリースノートには制約として「Maven 3.8.7 と比較すると、大規模なビルドで約 10% の速度低下が観察されました」とあるため、ローカルリポジトリの分割機能を使うことによる(極)多少のデメリットはあります。ですが、この機能によりアーティファクトのメンテナンスが各段に行いやすくなるのは間違いないため、10%くらいは目をつぶって常用していきたいと思います。

また、今回のように長いオプションをコマンドを実行する都度指定するのは面倒ですが、Mavenにはオプション指定を省略化する方法も用意されています。これについては「実行時オプションの固定化」で紹介していますので、合わせて確認いただければと思います。


参照資料


  1. pomにdependencyはありませんが、Mavenを実行するために必要なるプラグインモジュール等がリモートリポジトリから取得されます。 ↩︎

豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 基本から理解するJWTとJWT認証の仕組み (2022-12-08)
  2. AWS認定資格を12個すべて取得したので勉強したことなどをまとめます (2022-12-12)
  3. Nuxt3入門(第4回) - Nuxtのルーティングを理解する (2022-10-09)
  4. Viteベースの高速テスティングフレームワークVitestを使ってみる (2022-12-28)
  5. Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する (2022-10-28)
  6. ORマッパーのTypeORMをTypeScriptで使う (2022-07-27)
  7. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)
  8. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  9. GitHub Actions - 構成変数(環境変数)が外部設定できるようになったので用途を整理する (2023-01-16)
  10. Nuxt3入門(第7回) - Nuxt3のプラグイン・ミドルウェアを使う (2022-10-23)