Maven Assembly Plugin で配布用jar/zipを作成する方法

Maven Assembly Plugin で配布用jar/zipを作成する方法

はじめに

ソフトウェア開発において、ビルド成果物をどのように配布するかは重要な課題です。特にJavaアプリケーションの場合、コンパイル済みのクラスファイルだけでなく、依存するライブラリ、設定ファイル、実行スクリプト、ドキュメントなど、様々な要素をまとめてエンドユーザーに提供する必要があります。

MavenはJavaプロジェクトのビルドプロセスを管理するための強力なツールですが、標準の mvn package コマンドで作成されるJARファイルは、通常、プロジェクト自身のクラスファイルとリソースファイルのみを含み、依存するライブラリは含まれません。これは、依存ライブラリがリポジトリにインストールされている環境での使用を想定しているためです。しかし、多くの配布シナリオでは、依存ライブラリを含めた単一の実行可能JARファイル(通称 uber-JAR または fat-JAR)を作成したり、あるいはライブラリ群と実行スクリプト、設定ファイルなどをまとめたZIP/TARアーカイブを作成したりする必要があります。

ここで登場するのが Maven Assembly Plugin です。このプラグインは、プロジェクトの成果物、その依存関係、およびその他の必要なファイルを収集し、指定された形式(JAR, ZIP, TAR, ディレクトリなど)でパッケージ化することを目的としています。これにより、複雑な構成を持つアプリケーションの配布物を、Mavenビルドプロセスの一部として自動的に作成できるようになります。

この記事では、Maven Assembly Pluginの基本的な使い方から、カスタム descriptor を利用した柔軟な配布物作成、さらに高度な設定やよくある問題、関連する他のプラグインとの比較まで、詳細かつ網羅的に解説します。この記事を読むことで、あなたのプロジェクトに最適な配布パッケージをAssembly Pluginを使って効率的に作成できるようになるでしょう。

Maven Assembly Pluginとは

Maven Assembly Plugin(maven-assembly-plugin)は、Mavenプロジェクトのビルド成果物やその依存関係、その他のファイルなどを集約し、様々な形式のアーカイブ(zip, tar.gz, jarなど)やディレクトリ構造として出力するためのMavenプラグインです。

標準のMavenビルドサイクルにおいて、package フェーズでは通常、Maven JAR PluginやMaven WAR Pluginなどによって、プロジェクト自身が生成したクラスファイルやリソースファイルをまとめたアーカイブが作成されます。しかし、これらの標準プラグインは依存ライブラリをアーカイブに含めたり、実行スクリプトを追加したりといった、配布物としての最終的な形を整える機能は持ちません。

Assembly Pluginは、この「最終的な配布物の作成」という役割を担います。その核心となるのは アセンブリ descriptor と呼ばれるXMLファイルです。この descriptor ファイルに、どのようなファイルを(プロジェクトの成果物、依存ライブラリ、その他のファイルなど)、どのような構造で、どの形式のアーカイブに含めるかを詳細に記述します。

Assembly Pluginは、この descriptor に従って、以下の要素を収集し、再配置し、指定された形式でパッケージ化します。

  1. プロジェクトの成果物: mvn package で作成されたJAR/WAR/EARなどの主要成果物。
  2. プロジェクトの依存関係: pom.xml に定義された各種依存ライブラリ。スコープによるフィルタリングも可能。
  3. プロジェクト内のファイル/ディレクトリ: ソースディレクトリ、リソースディレクトリ、その他のカスタムディレクトリ内のファイルなど。特定のパターンでファイルを指定できます。
  4. プロジェクト外のファイル: ビルドプロセスとは直接関係ない、外部の特定のファイル。

これらの要素を組み合わせることで、例えば以下のような様々な配布物を自動生成できます。

  • 依存ライブラリを全て含んだ単一の実行可能JARファイル
  • 実行スクリプト、設定ファイル群、ライブラリディレクトリを含むZIPアーカイブ
  • ソースコードとドキュメントをまとめたTAR.GZアーカイブ
  • 特定の形式に整形されたディレクトリ構造

基本的な使い方

Maven Assembly Pluginを使用するには、まずプロジェクトの pom.xml にプラグインの設定を追加します。

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version> <!-- 最新のバージョンを使用推奨 -->
<configuration>
<!-- ここにプラグインの全体的な設定を記述 -->
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- 任意のID -->
<phase>package</phase> <!-- 通常はpackageフェーズにバインド -->
<goals>
<goal>single</goal> <!-- アセンブリを一つ作成するゴール -->
</goals>
<configuration>
<!-- ここにこのexecution固有の設定を記述 -->
<!-- descriptorRef または descriptors を指定 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

上記の例では、maven-assembly-pluginbuild 要素内の plugins セクションに追加しています。バージョンは最新の安定版を使用することをお勧めします(例では 3.6.0)。

executions セクションでは、プラグインの実行タイミングとゴールを指定します。
* id: 実行構成を一意に識別するための任意のIDです。
* phase: この実行をMavenのどのビルドフェーズに関連付けるかを指定します。一般的には、成果物が作成される package フェーズ、またはデプロイ前の install フェーズにバインドすることが多いです。
* goals: この実行でAssembly Pluginのどのゴールを実行するかを指定します。最もよく使われるのは single ゴールで、これは指定された descriptor に従って一つだけアセンブリを作成します。

Assembly Pluginにどのようなアセンブリを作成させるかは、configuration セクション、特に executions 内の configuration で指定します。アセンブリの定義方法は、主に以下の2通りがあります。

  1. 定義済み Descriptor (descriptorRef): Assembly Pluginにあらかじめ用意されている標準の descriptor を参照します。手軽に使えます。
  2. カスタム Descriptor (descriptors): 独自の descriptor XMLファイルを作成し、それを指定します。非常に柔軟な設定が可能です。

まずは、定義済み descriptor から見ていきましょう。

定義済み Descriptor (Pre-defined Descriptors)

Assembly Pluginには、よくある配布シナリオに対応するための定義済み descriptor がいくつか用意されています。これらを使用する場合、カスタム descriptor ファイルを作成する必要はなく、pom.xmldescriptorRef 要素を使って参照するだけです。

主な定義済み descriptor には以下があります。

  • jar-with-dependencies: プロジェクトのクラスファイルとすべての依存ライブラリを単一の実行可能(設定が必要)または通常のJARファイルにまとめる。
  • bin: 実行可能なアプリケーションのバイナリ配布物を作成する。通常、実行スクリプト、ライブラリディレクトリ、設定ファイルなどをまとめたZIP/TAR形式になる。
  • src: プロジェクトのソースコードとリソースファイルをまとめた配布物を作成する。

それぞれの使い方を見ていきましょう。

1. jar-with-dependencies

これは最も頻繁に使われる定義済み descriptor の一つです。プロジェクト自身が作成したJARファイルと、その依存するライブラリをすべて含んだ単一のJARファイルを作成します。これは、アプリケーションを単一ファイルとして配布したい場合に非常に便利です。

ただし、標準の jar-with-dependencies descriptor で作成されるJARファイルは、デフォルトでは実行可能ではありません。実行可能にするには、後述する archive 設定で Main-Class を指定する必要があります。

pom.xml 設定例:

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRef>jar-with-dependencies</descriptorRef> <!-- ここで指定 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

この設定で mvn clean package を実行すると、target ディレクトリ内に YOUR_ARTIFACT_ID-YOUR_VERSION-jar-with-dependencies.jar という名前のファイルが作成されます。このファイルには、あなたのプロジェクトのクラスファイルと、pom.xml で定義されたすべての compile および runtime スコープの依存ライブラリが展開された状態で含まれています。

注意点: 依存ライブラリをすべて展開して単一JARにまとめるため、複数の依存ライブラリが同じファイルパス(例: META-INF/services 内のファイルや、プロパティファイルなど)を持つ場合に競合が発生する可能性があります。多くの場合、後から読み込まれたファイルが優先されるなど、予期しない動作を引き起こすことがあります。このようなクラスパスの競合をより詳細に制御したい場合は、Shade Pluginを検討する必要があります(Shade Pluginとの比較は後述します)。

実行可能JARにするには:
jar-with-dependencies descriptor だけでは、単に依存ライブラリをまとめたJARが作成されるだけで、実行可能 (java -jar YOUR_JAR_FILE) にはなりません。実行可能にするには、archive 要素を使って MANIFEST.MF ファイルに Main-Class エントリを追加する必要があります。

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRef>jar-with-dependencies</descriptorRef>
<archive> <!-- ここから追加 -->
<manifest>
<mainClass>com.example.yourpackage.YourMainClass</mainClass> <!-- メインクラスの完全修飾名 -->
</manifest>
</archive> <!-- ここまで -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

この設定を追加することで、作成されたJARファイルを実行可能にできます。

2. bin

bin descriptor は、実行可能なアプリケーションの配布物(バイナリ配布物)を作成するために使用されます。これは通常、単一のファイルではなく、実行スクリプト、設定ファイル群、ライブラリをまとめたディレクトリ構造や、それをZIP/TAR形式でアーカイブした形になります。

bin descriptor は、デフォルトで以下の構造を含むZIPアーカイブを作成します(これは descriptor の内部定義によります)。

YOUR_ARTIFACT_ID-YOUR_VERSION/
├── bin/ (実行スクリプトなど)
├── lib/ (依存ライブラリ)
└── YOUR_ARTIFACT_ID-YOUR_VERSION.jar (プロジェクト自身の成果物JAR)

pom.xml 設定例:

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRef>bin</descriptorRef> <!-- ここで指定 -->
<!-- オプション: 実行スクリプトなどのファイルを追加したい場合はカスタムdescriptorにするか、
カスタムdescriptorをbin descriptorのコンポーネントとして利用するなど工夫が必要 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

この設定で mvn clean package を実行すると、target ディレクトリ内に YOUR_ARTIFACT_ID-YOUR_VERSION-bin.zip という名前のZIPファイルが作成されます。このZIPを展開すると、上記のようなディレクトリ構造が現れます。

bin descriptor は、アプリケーションのバイナリ配布物のための基本的な構造を提供しますが、多くの場合、独自の実行スクリプトや設定ファイルを含めたいでしょう。その場合は、bin descriptor をそのまま使うのではなく、カスタム descriptor を作成して、必要なファイルやディレクトリ構造を定義する必要があります。あるいは、カスタム descriptor から bin descriptor をコンポーネントとして参照する、といった高度な使い方も考えられます。

3. src

src descriptor は、プロジェクトのソースコードとリソースファイルをまとめた配布物を作成するために使用されます。これは、プロジェクトのソースを公開したい場合や、開発者向けの配布物として使用する場合に役立ちます。

src descriptor は、デフォルトでプロジェクトの src/main/java, src/main/resources, src/test/java, src/test/resources, pom.xml など、ソース関連のファイルをまとめたZIPアーカイブを作成します。

pom.xml 設定例:

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRef>src</descriptorRef> <!-- ここで指定 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

この設定で mvn clean package を実行すると、target ディレクトリ内に YOUR_ARTIFACT_ID-YOUR_VERSION-src.zip という名前のZIPファイルが作成されます。

カスタム Descriptor (Custom Descriptors)

定義済み descriptor は手軽ですが、配布物の構造や内容を細かく制御したい場合には、カスタム descriptor を作成する必要があります。カスタム descriptor はXMLファイルで記述し、Assembly Pluginの豊富な設定要素を駆使して、理想の配布物構造を定義します。

なぜカスタム descriptor が必要か?

  • 定義済み descriptor の構造や内容が要件に合わない場合
  • 独自の実行スクリプト、設定ファイル、ドキュメントなどを含めたい場合
  • 依存ライブラリの含め方(展開するか、libディレクトリに入れるか、など)を制御したい場合
  • 複数の形式(ZIPとTAR.GZなど)で配布物を作成したい場合
  • 特定のファイルやディレクトリのパーミッションを設定したい場合

カスタム Descriptor ファイルの作成

カスタム descriptor ファイルは、プロジェクト内の任意の場所に配置できますが、慣習的には src/main/assembly/ ディレクトリ内に .xml 拡張子で作成することが多いです。例えば、src/main/assembly/assembly.xml というファイルを作成します。

pom.xml でこのカスタム descriptor ファイルを指定するには、descriptorRef の代わりに descriptors 要素を使用します。

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor> <!-- カスタム descriptor のパス -->
</descriptors>
<!-- または <descriptor>assembly.xml</descriptor> と相対パスで指定することも可能 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

これで、mvn clean package を実行すると、指定した src/main/assembly/assembly.xml の定義に基づいてアセンブリが作成されるようになります。

Descriptor ファイルの基本構造

カスタム descriptor ファイル(例: src/main/assembly/assembly.xml)は、以下の基本的なXML構造を持ちます。

“`xml

<!-- アセンブリに一意なID。出力ファイル名に付加される。 -->
<id>custom</id>

<!-- 作成するアセンブリの形式 (zip, tar.gz, jar, dirなど) -->
<formats>
    <format>zip</format>
    <format>tar.gz</format>
</formats>

<!-- アセンブリのルートディレクトリを作成するかどうか (true: プロジェクト名-バージョン/..., false: ... ) -->
<includeBaseDirectory>true</includeBaseDirectory>

<!-- ここにアセンブリに含めるファイルや依存関係を定義 -->
<!-- fileSets, dependencySets, files, component など -->


“`

  • <assembly>: descriptor ファイルのルート要素です。スキーマ定義を指定します。
  • <id>: このアセンブリ descriptor に一意な識別子を与えます。作成されるアーカイブファイル名に artifactId-version-id.format の形式で付加されます(例: myproject-1.0.0-custom.zip)。
  • <formats>: 作成したいアセンブリの形式を指定します。複数の形式を指定することも可能です。一般的な形式には zip, tar.gz, tar.bz2, jar, dir, livelihoods などがあります。
  • <includeBaseDirectory>: true に設定すると、作成されるアーカイブのルートにプロジェクト名とバージョンを含むディレクトリ(例: myproject-1.0.0/)を作成し、その中にコンテンツを配置します。false に設定すると、コンテンツがアーカイブのルートに直接配置されます。デフォルトは true です。

これらの基本要素に加え、アセンブリに含める具体的なコンテンツを定義するために、以下の要素を <assembly> の直下に追加します。

  • <fileSets>: プロジェクトディレクトリ内の特定のファイルやディレクトリ群を含めるために使用します。
  • <dependencySets>: プロジェクトの依存関係(ライブラリJARなど)を含めるために使用します。
  • <files>: プロジェクトディレクトリ外の特定の単一ファイルを含めるために使用します。
  • <component>: 別途定義されたアセンブリコンポーネントを参照するために使用します。

主要な要素の詳細解説

<fileSets>

<fileSets> 要素は、プロジェクトのビルドディレクトリ (${project.basedir} または ${project.build.directory}) 内にあるファイルやディレクトリ群を指定して、アセンブリに含めるために使用します。これを使って、設定ファイル、実行スクリプト、ドキュメントなどを配布物に追加できます。

<fileSets> は複数の <fileSet> 子要素を持つことができます。各 <fileSet> 要素は、一つのファイルセットを定義します。

<fileSet> の主な子要素:

  • <directory>: ファイルセットの基点となるディレクトリを指定します。通常、${project.basedir} (プロジェクトルート) や ${project.build.directory} (targetディレクトリ) を基準とした相対パスで指定します。例: src/main/resources, src/main/bin, target/classes など。
  • <outputDirectory>: <directory> で指定したファイルのセットを、アセンブリ内のどのディレクトリに配置するかを指定します。アーカイブのルートからの相対パスです。例: /, config, bin など。デフォルトは / です。
  • <includes>: 含めるファイルを Antスタイルのパターンで指定します。複数の <include> 子要素を持つことができます。例: *.properties, **/*.xml, scripts/*.sh
  • <excludes>: 除外するファイルを Antスタイルのパターンで指定します。複数の <exclude> 子要素を持つことができます。例: *.bak, **/temp/*
  • <fileMode>: このファイルセットに含まれるファイルに設定するファイルモード(パーミッション)を八進数で指定します(例: 0644)。特に実行スクリプトなどに実行権限 (0755) を付けたい場合に便利です。
  • <directoryMode>: このファイルセットに含まれるディレクトリに設定するディレクトリモードを八進数で指定します(例: 0755)。
  • <filtered>: このファイルセットに含まれるファイルを Mavenのリソースフィルタリングと同様に処理するかどうかを指定します (true または false)。true にすると、${project.version} のような Mavenプロパティや、${variable} のような独自のプロパティがファイル内容で置換されます。デフォルトは false です。

<fileSets> 使用例1: 設定ファイルを含める

プロジェクトルートに config ディレクトリがあり、その中の .properties ファイルと .xml ファイルをアセンブリの conf ディレクトリに含める例。

xml
<fileSets>
<fileSet>
<directory>${project.basedir}/config</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*.properties</include>
<include>*.xml</include>
</includes>
<fileMode>0644</fileMode>
</fileSet>
</fileSets>

<fileSets> 使用例2: 実行スクリプトを含める(実行権限付き)

プロジェクトルートに src/main/bin ディレクトリがあり、その中の .sh ファイルと .bat ファイルをアセンブリの bin ディレクトリに含める例。Unix系スクリプトには実行権限を付けます。

xml
<fileSets>
<fileSet>
<directory>${project.basedir}/src/main/bin</directory>
<outputDirectory>bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<fileMode>0755</fileMode> <!-- 実行権限を付与 -->
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/bin</directory>
<outputDirectory>bin</outputDirectory>
<includes>
<include>*.bat</include>
</includes>
<fileMode>0644</fileMode> <!-- Windowsバッチファイルは実行権限不要 -->
</fileSet>
</fileSets>

<fileSets> 使用例3: フィルタリングを有効にして含める

プロジェクトルートの src/main/config ディレクトリにある application.properties ファイルをアセンブリのルートディレクトリに含め、ファイル内でMavenプロパティを置換する例。

xml
<fileSets>
<fileSet>
<directory>${project.basedir}/src/main/config</directory>
<outputDirectory>/</outputDirectory> <!-- アセンブリのルート -->
<includes>
<include>application.properties</include>
</includes>
<filtered>true</filtered> <!-- フィルタリングを有効化 -->
<fileMode>0644</fileMode>
</fileSet>
</fileSets>

この場合、application.properties の内容に ${project.version} のような文字列があれば、ビルド時のプロジェクトバージョンで置き換えられます。

<dependencySets>

<dependencySets> 要素は、プロジェクトの依存関係(pom.xml<dependencies> で定義されたライブラリ)をアセンブリに含めるために使用します。これを使って、アプリケーションが必要とするライブラリ群を配布物に含めることができます。

<dependencySets> は複数の <dependencySet> 子要素を持つことができます。各 <dependencySet> 要素は、一つの依存関係セットを定義します。

<dependencySet> の主な子要素:

  • <outputDirectory>: この依存関係セットに含まれる依存関係を、アセンブリ内のどのディレクトリに配置するかを指定します。例: lib, / など。デフォルトは / です。
  • <includes>: 含める依存関係を groupId:artifactId:type:classifier の形式で指定します。ワイルドカード (*) も使用可能です。複数の <include> 子要素を持つことができます。例: com.example:mylib:*, *:*:jar:tests
  • <excludes>: 除外する依存関係を <includes> と同じ形式で指定します。複数の <exclude> 子要素を持つことができます。例: junit:junit:*, org.slf4j:*
  • <useTransitiveDependencies>: 推移的な依存関係を含めるかどうかを指定します (true または false)。デフォルトは true です。
  • <useProjectArtifact>: プロジェクト自身の成果物JARをこのセットに含めるかどうかを指定します (true または false)。通常は <fileSets> などで別途含めるため false にすることが多いですが、<outputDirectory> を分けて含めたい場合などに使用します。デフォルトは true です。
  • <unpack>: この依存関係セットに含まれる各依存JARファイルを、アーカイブせずに展開して含めるかどうかを指定します (true または false)。jar-with-dependencies のように単一JARにすべてをまとめたい場合は true にします。通常は false に設定し、ライブラリを個別のJARファイルとして含めます。デフォルトは false です。
  • <scope>: 含める依存関係のスコープを指定します。指定できる値は compile, runtime, test, provided, system, import です。デフォルトは runtime スコープの依存関係(compile も含む)です。

<dependencySets> 使用例1: 実行に必要なすべてのライブラリを lib ディレクトリに含める

プロジェクトの runtime スコープの依存関係をすべてアセンブリの lib ディレクトリに含める例。プロジェクト自身の成果物JARは含めません。

xml
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<useProjectArtifact>false</useProjectArtifact> <!-- プロジェクト自身のJARは含めない -->
<unpack>false</unpack> <!-- 依存JARを展開せずそのまま含める -->
<scope>runtime</scope> <!-- runtimeスコープの依存関係のみ -->
</dependencySet>
</dependencySets>

<dependencySets> 使用例2: 特定のグループIDのライブラリのみ含める

com.example グループIDを持つ依存関係のみを external-lib ディレクトリに含める例。

xml
<dependencySets>
<dependencySet>
<outputDirectory>external-lib</outputDirectory>
<includes>
<include>com.example:*</include>
</includes>
<unpack>false</unpack>
</dependencySet>
</dependencySets>

<dependencySets> 使用例3: 依存JARを展開して単一ディレクトリにフラットに含める

依存JARを展開してアセンブリのルートディレクトリにすべてフラットに含める例。これは jar-with-dependencies に似た動作の一部をカスタムで行う場合に考えられます。

xml
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory> <!-- アセンブリのルート -->
<unpack>true</unpack> <!-- 依存JARを展開 -->
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact> <!-- プロジェクト自身のJARは含めないことが多い -->
</dependencySet>
</dependencySets>

unpack=true を使うと、<fileSets> で含めたファイル(例: プロジェクト自身のクラスファイルやリソース)と依存ライブラリが同じディレクトリ構造に展開されるため、単一のディレクトリ内でクラスパスを解決できるようになります。Executable JARを作成する際に、プロジェクト自身のJARと依存ライブラリを同じディレクトリに展開し、META-INF/MANIFEST.MF の Class-Path を設定しない形で単一ディレクトリ配布物を作る、といったシナリオで役立ちます。ただし、通常は jar-with-dependencies で単一JARにするか、unpack=falselib ディレクトリにまとめるか、のどちらかを選択することが多いでしょう。

<files>

<files> 要素は、プロジェクトディレクトリ外にある特定の単一ファイルをアセンブリに含めるために使用します。これは、ライセンスファイルや README ファイルなど、プロジェクトのソースツリーやビルド成果物ディレクトリに含まれていないファイルを配布物に追加したい場合に便利です。

<files> は複数の <file> 子要素を持つことができます。各 <file> 要素は、一つのファイルを含めることを定義します。

<file> の主な子要素:

  • <source>: 含めるファイルの絶対パスまたはプロジェクトのベースディレクトリからの相対パスを指定します。
  • <outputDirectory>: そのファイルをアセンブリ内のどのディレクトリに配置するかを指定します。例: /, docs など。デフォルトは / です。
  • <destName>: アセンブリ内でファイルに付ける名前を指定します。省略すると <source> で指定したファイル名がそのまま使用されます。
  • <fileMode>: ファイルモード(パーミッション)を八進数で指定します(例: 0644)。
  • <filtered>: ファイル内容をフィルタリングするかどうかを指定します (true または false)。

<files> 使用例: READMEとLICENSEファイルを含める

プロジェクトルートの README.mdLICENSE.txt ファイルをアセンブリのルートディレクトリに含める例。

xml
<files>
<file>
<source>${project.basedir}/README.md</source>
<outputDirectory>/</outputDirectory>
<fileMode>0644</fileMode>
</file>
<file>
<source>${project.basedir}/LICENSE.txt</source>
<outputDirectory>/</outputDirectory>
<fileMode>0644</fileMode>
</file>
</files>

<component>

<component> 要素は、別途定義されたアセンブリコンポーネントを参照するために使用します。コンポーネントは、<fileSets>, <dependencySets>, <files> などの要素の集まりを定義した独立したXMLファイルです。これにより、共通のアセンブリ定義の一部を再利用したり、複数のdescriptor ファイル間で定義を共有したりできます。

コンポーネントファイルは通常、src/main/assembly/components/ ディレクトリなどに配置します。コンポーネントファイルは <component> ルート要素を持ち、その中に <fileSets>, <dependencySets> などを定義します。

例:コンポーネントファイル (src/main/assembly/components/my-component.xml)

“`xml

<fileSets>
    <fileSet>
        <directory>${project.basedir}/config</directory>
        <outputDirectory>conf</outputDirectory>
    </fileSet>
</fileSets>

<dependencySets>
    <dependencySet>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
</dependencySets>


“`

カスタム descriptor ファイル (src/main/assembly/assembly.xml) からこのコンポーネントを参照する例:

“`xml

<id>composed</id>
<formats>
    <format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>

<component>components/my-component.xml</component> <!-- コンポーネントファイルへのパス -->

<!-- コンポーネントに追加でファイルを含めることも可能 -->
<fileSets>
    <fileSet>
        <directory>${project.basedir}/docs</directory>
        <outputDirectory>docs</outputDirectory>
    </fileSet>
</fileSets>


“`

このように <component> を使うことで、descriptor ファイルの記述をモジュール化し、管理しやすくすることができます。

具体的なカスタム Descriptor 作成例

ここからは、より実践的なカスタム descriptor の作成例をいくつか紹介します。

例1: アプリケーション配布用ZIP (実行スクリプト、設定ファイル、ライブラリディレクトリ)

多くのエンタープライズアプリケーションの配布では、単一JARではなく、実行スクリプト、設定ファイル、ライブラリをまとめたZIP/TARアーカイブが一般的です。

src/main/assembly/app-distribution.xml:

“`xml

<id>app-dist</id> <!-- 出力ファイル名は artifactId-version-app-dist.zip/tar.gz となる -->
<formats>
    <format>zip</format>
    <format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory> <!-- プロジェクト名-バージョン のルートディレクトリを作成 -->

<fileSets>
    <!-- 実行スクリプト (src/main/bin から app/bin へ) -->
    <fileSet>
        <directory>${project.basedir}/src/main/bin</directory>
        <outputDirectory>app/bin</outputDirectory>
        <includes>
            <include>run.sh</include>
            <include>run.bat</include>
        </includes>
        <fileMode>0755</fileMode> <!-- Unix系スクリプトに実行権限 -->
    </fileSet>

    <!-- 設定ファイル (src/main/config から app/conf へ、フィルタリング有効) -->
    <fileSet>
        <directory>${project.basedir}/src/main/config</directory>
        <outputDirectory>app/conf</outputDirectory>
        <includes>
            <include>application.properties</include>
            <include>log4j2.xml</include>
        </includes>
        <filtered>true</filtered>
        <fileMode>0644</fileMode>
    </fileSet>

    <!-- その他リソース (src/main/resources から app/resources へ) -->
     <fileSet>
        <directory>${project.basedir}/src/main/resources</directory>
        <outputDirectory>app/resources</outputDirectory>
        <includes>
            <include>**/*</include>
        </includes>
        <excludes>
            <exclude>**/*.properties</exclude> <!-- configで含めるものは除く -->
            <exclude>**/*.xml</exclude>
        </excludes>
        <fileMode>0644</fileMode>
    </fileSet>

     <!-- プロジェクト自身の成果物JAR (app ディレクトリ直下へ) -->
    <fileSet>
        <directory>${project.build.directory}</directory>
        <outputDirectory>app</outputDirectory>
        <includes>
            <include>${project.build.finalName}.jar</include> <!-- target/artifactId-version.jar -->
        </includes>
        <fileMode>0644</fileMode>
    </fileSet>
</fileSets>

<dependencySets>
    <!-- 依存ライブラリ (lib ディレクトリへ) -->
    <dependencySet>
        <outputDirectory>lib</outputDirectory> <!-- app/lib ではない点に注意 (includeBaseDirectory=true の影響) -->
        <useProjectArtifact>false</outputDirectory> <!-- プロジェクト自身のJARは上記fileSetで含める -->
        <unpack>false</unpack>
        <scope>runtime</scope>
    </dependencySet>
</dependencySets>

<files>
    <!-- ライセンスとREADMEをルートディレクトリ直下へ -->
    <file>
        <source>${project.basedir}/LICENSE.txt</source>
        <outputDirectory>/</outputDirectory>
        <fileMode>0644</fileMode>
    </file>
     <file>
        <source>${project.basedir}/README.md</source>
        <outputDirectory>/</outputDirectory>
        <fileMode>0644</fileMode>
    </file>
</files>


“`
出力されるアーカイブ構造の例:

myproject-1.0.0/
├── app/
│ ├── bin/
│ │ ├── run.sh
│ │ └── run.bat
│ ├── conf/
│ │ ├── application.properties (フィルタリング済み)
│ │ └── log4j2.xml (フィルタリング済み)
│ ├── resources/
│ │ └── ... (その他のリソース)
│ └── myproject-1.0.0.jar (プロジェクト自身のJAR)
├── lib/ (依存ライブラリ群)
│ ├── spring-core-6.0.0.jar
│ ├── log4j-api-2.20.0.jar
│ └── ...
├── LICENSE.txt
└── README.md

この例では、includeBaseDirectorytrue にしているため、出力のルートに myproject-1.0.0/ ディレクトリが作成されます。その中に、アプリケーション本体のJAR (app/myproject-1.0.0.jar)、実行スクリプト (app/bin/)、設定ファイル (app/conf/)、その他のリソース (app/resources/) を配置しています。依存ライブラリは、別途 lib/ ディレクトリにまとめています。LICENSEファイルとREADMEファイルはルートディレクトリ直下に配置しています。

Maven Assembly Pluginの設定において、<outputDirectory> のパスは <includeBaseDirectory> の設定に影響を受ける点に注意が必要です。includeBaseDirectorytrue の場合、<outputDirectory>app/bin</outputDirectory> は最終的に YOUR_BASE_DIRECTORY/app/bin に配置されます。includeBaseDirectoryfalse の場合は、アーカイブのルートからの相対パスとなります。

例2: 実行可能JAR (jar-with-dependencies + MANIFEST.MF)

定義済み descriptor の jar-with-dependencies をカスタム descriptor で実現しつつ、実行可能にするための MANIFEST.MF 設定を追加する例。

src/main/assembly/executable-jar.xml:

“`xml

<id>executable</id>
<formats>
    <format>jar</format> <!-- JAR形式で出力 -->
</formats>
<includeBaseDirectory>false</includeBaseDirectory> <!-- JAR形式なのでルートディレクトリは不要 -->

<dependencySets>
    <dependencySet>
        <outputDirectory>/</outputDirectory> <!-- ルートディレクトリに展開 -->
        <useProjectArtifact>true</useProjectArtifact> <!-- プロジェクト自身の成果物もこのセットに含める -->
        <unpack>true</unpack> <!-- 依存JARを展開 -->
        <scope>runtime</scope>
    </dependencySet>
</dependencySets>

<archive> <!-- JARのメタ情報(MANIFEST.MF)を設定 -->
    <manifest>
        <mainClass>com.example.yourpackage.YourMainClass</mainClass> <!-- メインクラスの完全修飾名 -->
        <!-- <addClasspath>true</addClasspath> --> <!-- 依存ライブラリをlibディレクトリなどに配置し、クラスパスを記述する場合 -->
        <!-- <classpathPrefix>lib/</classpathPrefix> --> <!-- addClasspath=trueの場合のプレフィックス -->
    </manifest>
     <!-- MANIFEST.MF に追加のエントリを設定 -->
    <manifestEntries>
        <Implementation-Title>${project.name}</Implementation-Title>
        <Implementation-Version>${project.version}</Implementation-Version>
        <Build-Timestamp>${maven.build.timestamp}</Build-Timestamp> <!-- propertiesにmaven.build.timestampが必要 -->
    </manifestEntries>
</archive>


``
この例では、
jarとすることで出力形式をJARに指定しています。falseとすることで、JARのルートに直接ファイルやクラスが配置されます。trueとし、trueとすることで、プロジェクト自身のクラスファイルとすべての依存ライブラリをJARのルートに展開して含めます。これにより、単一のフラットな構造を持つJARが作成されます。要素内でを設定することで、JARを実行可能にします。
さらに、
でカスタムのMANIFEST.MFエントリを追加しています。${project.name},${project.version},${maven.build.timestamp}` といったMavenプロパティも使用可能です。

注意: unpack=true を使うと、前述の通りクラスパスの競合(ファイルの上書きなど)が発生しやすいです。特にSpring Bootのexecutable jarのように、複数のライブラリに含まれる同じファイル(例: /META-INF/spring.handlers)をマージする必要がある場合は、Assembly PluginではなくShade Pluginを使う方が適していることが多いです。Shade Pluginは、リソース変換機能など、Uber JAR作成に特化した高度なマージ機能を提供します。

例3: 設定ファイルのみのZIP

アプリケーションの設定ファイル群のみをまとめたZIPアーカイブを作成する例。これは、デプロイ時に設定ファイルだけを更新したい場合などに使用できます。

src/main/assembly/config-only.xml:

“`xml

<id>config</id>
<formats>
    <format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>

<fileSets>
    <!-- 設定ファイル (src/main/config から conf へ、フィルタリング有効) -->
    <fileSet>
        <directory>${project.basedir}/src/main/config</directory>
        <outputDirectory>conf</outputDirectory>
        <includes>
            <include>*.properties</include>
            <include>*.xml</include>
        </includes>
        <filtered>true</filtered>
        <fileMode>0644</fileMode>
    </fileSet>
</fileSets>

<!-- このアセンブリには依存関係やビルド成果物は含めない -->


``
この例では
要素のみを使用し、必要な設定ファイルだけを含めています。` 要素は含まれていません。

Descriptor のパス指定について

pom.xml<descriptors> 要素で descriptor ファイルのパスを指定する場合、デフォルトでは ${project.basedir} からの相対パスと解釈されます。つまり <descriptor>src/main/assembly/assembly.xml</descriptor> はプロジェクトルートからのパスとして扱われます。
プラグインの <configuration><descriptorSourceDirectory>...</descriptorSourceDirectory> を設定することで、descriptor ファイルを配置する基準ディレクトリを変更することも可能です。例えば <descriptorSourceDirectory>${project.build.directory}/generated-assemblies</descriptorSourceDirectory> のように指定すると、生成されたdescriptor ファイルを使用できます。

高度な使い方と設定

カスタム descriptor の要素以外にも、Assembly Pluginには様々な設定オプションがあります。

archive 要素

前述の実行可能JARの例でも登場しましたが、<archive> 要素は作成するアーカイブ(特にJAR形式)のメタ情報(MANIFEST.MFなど)を制御するために使用します。<archive><assembly> の子要素として配置します。

主な子要素:

  • <manifest>: JARの META-INF/MANIFEST.MF を設定します。
    • <mainClass>: 実行可能JARのエントリポイントとなるクラスの完全修飾名を指定します。
    • <addClasspath>: 依存ライブラリをJARに含めずに、外部のライブラリディレクトリを参照する Class-Path エントリを生成するかを指定します (true/false)。
    • <classpathPrefix>: addClasspathtrue の場合に、Class-Path エントリに付加するプレフィックスを指定します。通常、ライブラリディレクトリの相対パスを指定します(例: lib/)。
    • <packageName>: (非推奨)
  • <manifestEntries>: META-INF/MANIFEST.MF にカスタムエントリを追加します。キーと値を <entry> 子要素で指定します。

フィルタリングとプレースホルダ (filtered)

<fileSet><file> 要素の <filtered>true</filtered> を設定すると、そのファイルの内容に対して Mavenのリソースフィルタリングが適用されます。ファイル内の ${property} 形式のプレースホルダは、Mavenプロパティ(${project.version}, ${project.groupId}, ${project.artifactId} など)、システムプロパティ、ユーザー定義プロパティなどで置換されます。

カスタムプロパティを使用したい場合は、pom.xml<properties> セクションで定義するか、コマンドラインで -DpropertyName=value のように指定します。

“`xml

MyApplication
jdbc:h2:mem:testdb

org.apache.maven.plugins
maven-assembly-plugin
3.6.0


make-assembly package
single



src/main/assembly/assembly.xml






“`

カスタム descriptor (src/main/assembly/assembly.xml) でフィルタリングを有効にするファイルセットを作成し、そのファイル(例: src/main/config/application.properties)に以下の内容を記述します。

“`properties

application.properties

app.name=${my.app.name}
app.version=${project.version}
database.url=${db.url}
“`

Assembly Pluginでフィルタリングが有効な状態でこのファイルが処理されると、出力される application.properties は以下のようになります(例: プロジェクトバージョンが 1.0.0 の場合)。

“`properties

application.properties

app.name=MyApplication
app.version=1.0.0
database.url=jdbc:h2:mem:testdb
“`
これにより、ビルド時にアプリケーションの設定ファイルを動的に生成できます。

ファイルモードとディレクトリモード (fileMode, directoryMode)

<fileSet>, <file>, <dependencySet> 要素には、それぞれ含めるファイルやディレクトリのパーミッションを設定するための <fileMode><directoryMode> 子要素があります。これらの値は八進数で指定します(例: 0644 は所有者読み書き、グループ読み取り、その他読み取り)。

これは特にUnix/Linux環境向けの配布物を作成する際に重要です。実行スクリプトには実行権限 (0755) を付与しないと、ユーザーがスクリプトを実行するために手動でパーミッションを変更する必要が出てきます。

xml
<fileSet>
<directory>${project.basedir}/src/main/bin</directory>
<outputDirectory>bin</outputDirectory>
<includes>
<include>run.sh</include>
</includes>
<fileMode>0755</fileMode> <!-- 実行権限を付与 -->
</fileSet>

注意: Windows環境ではファイルモードの概念が異なるため、これらの設定は無視されるか、意図した通りに機能しない場合があります。クロスプラットフォーム対応の配布物を作成する場合は、ターゲット環境での動作を確認する必要があります。

デタッチされたアセンブリ (attach)

デフォルトでは、Assembly Pluginによって作成されたアセンブリファイルは、そのプロジェクトのセカンダリ成果物としてMavenに「アタッチ」されます。これにより、mvn installmvn deploy コマンドを実行した際に、プロジェクトの主要成果物(例: メインのJARファイル)と一緒にローカルリポジトリにインストールされたり、リモートリポジトリにデプロイされたりします。

pom.xml の Assembly Plugin 設定で、<configuration> または <execution><configuration> 内に <attach> 要素を設定することで、この振る舞いを制御できます。

  • <attach>true</attach> (デフォルト): 作成されたアセンブリをプロジェクトのセカンダリ成果物としてアタッチします。
  • <attach>false</attach>: 作成されたアセンブリをアタッチしません。この場合、アセンブリファイルは target ディレクトリに生成されるだけですが、インストールやデプロイの対象にはなりません。ローカルでのみ配布物が必要な場合などに使用します。

通常はデフォルトの true のままにしておくことが多いでしょう。

複数アセンブリの作成

一つのプロジェクトで複数の種類のアセンブリ(例: アプリケーション配布ZIPとソースコードZIP)を作成したい場合があります。これを行うには、pom.xml<executions> セクションで複数の <execution> を定義し、それぞれの実行で異なる descriptor を指定します。

xml
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>make-app-assembly</id> <!-- 実行ID 1 -->
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/app-distribution.xml</descriptor> <!-- 1つ目のdescriptor -->
</descriptors>
</configuration>
</execution>
<execution>
<id>make-src-assembly</id> <!-- 実行ID 2 -->
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRef>src</descriptorRef> <!-- 2つ目のdescriptor (定義済み) -->
<!-- または <descriptors><descriptor>src/main/assembly/source.xml</descriptor></descriptors> -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

この設定により、mvn clean package を実行すると、app-distribution.xml に基づくアセンブリと src descriptor に基づくアセンブリの両方が作成され、アタッチされます。

よくある問題とトラブルシューティング

Assembly Pluginを使う上で遭遇しやすい問題とその解決策について説明します。

1. Main-Class が見つからない (Executable JARの場合)

jar-with-dependencies やカスタム descriptor で実行可能JARを作成しようとして、java -jar ... 実行時に Error: Could not find or load main class com.example.yourpackage.YourMainClass といったエラーが出る場合。

  • 確認点:
    • pom.xml<archive><manifest><mainClass>...</mainClass></manifest></archive> で指定したメインクラスの完全修飾名(パッケージ名を含むクラス名)が正しいか?スペルミスがないか?
    • 指定したメインクラスが実際にプロジェクトのソースに含まれているか?
    • そのクラスに public static void main(String[] args) メソッドが定義されているか?
    • <dependencySets><unpack>true</unpack> を設定し、プロジェクト自身の成果物と依存ライブラリを同じディレクトリに展開しているか、または <addClasspath>true</addClasspath><classpathPrefix> を適切に設定しているか?
    • Assembly Pluginが正しく実行され、META-INF/MANIFEST.MF ファイルが作成され、Main-Class エントリが含まれているか?作成されたJARファイルを解凍して確認できます。

2. 依存関係が漏れている/余計に含まれる

配布物に含めたい依存ライブラリが含まれていない、あるいは含めたくないライブラリが含まれてしまう場合。

  • 確認点:
    • <dependencySet><scope> 設定が適切か?デフォルトは runtime (および compile) です。provided スコープのライブラリは通常含まれません。
    • <includes><excludes> パターンが適切か?誤って必要なライブラリを除外したり、不要なライブラリを含めたりしていないか?
    • 推移的な依存関係が必要な場合、<useTransitiveDependencies>true</useTransitiveDependencies> (デフォルト) になっているか?不要な場合は false に設定します。
    • プロジェクト自身の成果物を含めるか含めないか (<useProjectArtifact>) の設定は意図通りか?
    • mvn dependency:tree コマンドを実行して、プロジェクトの実際の依存関係ツリーを確認し、含めたい/含めたくないライブラリがどのスコープで定義されているかを確認します。

3. ファイルパスの問題

<fileSet><file> で指定したファイルがアセンブリに含まれない、あるいは意図した場所に配置されない場合。

  • 確認点:
    • <directory><source> で指定したパスは正しいか?特に ${project.basedir}${project.build.directory} からの相対パスが正しいか確認します。
    • <outputDirectory> で指定したアセンブリ内の配置先パスは正しいか?includeBaseDirectory の設定も考慮します。
    • <includes><excludes> の Antスタイルパターンが正しいか?意図したファイルがパターンにマッチしているか確認します。
    • mvn assembly:single -X (デバッグモード) で実行し、ログの詳細を確認します。Assembly Pluginがどのファイルを収集しようとしたかの情報が得られます。

4. パーミッションの問題

実行スクリプトなどに実行権限が付与されない、あるいは不要なパーミッションが付与される場合。

  • 確認点:
    • <fileSet><file><fileMode> 設定が記述されているか?
    • <fileMode> の値が八進数で正しく指定されているか?(例: 実行権限は 0755
    • 対象のファイルセット/ファイルが、この <fileMode> 設定を持つ要素に含まれているか?

5. クラスパスの競合 (Shade Pluginとの比較)

特に unpack=true で単一JARを作成する場合に、異なる依存ライブラリに含まれる同じ名前のファイルが上書きされて問題が発生する場合。

  • 解決策:
    • Assembly Pluginの unpack=true で発生する単純な上書きは、ほとんどの場合回避できません。
    • このようなクラスパスの競合(特に META-INF/services ファイルや、カスタムコンフィグファイルなど、マージが必要なファイル)を適切に処理するには、Maven Shade Plugin を使用することを検討してください。Shade Pluginは、リソース変換やクラスのシェーディング(パッケージ名の変更)など、Uber JAR作成に特化したより高度な機能を提供します。Assembly Pluginは主に「ファイルをまとめる」ことに長けており、Shade Pluginは「バイトコードやリソースを操作しながら一つにまとめる」ことに長けています。

Shade Plugin との比較

Maven Assembly Pluginと Maven Shade Plugin は、どちらも依存ライブラリを成果物と一緒にパッケージ化する目的で使用されることがありますが、そのアプローチと得意なことが異なります。

  • Maven Assembly Plugin:

    • 目的: プロジェクト成果物、依存関係、その他のファイルを収集し、指定された構造でアーカイブまたはディレクトリとしてパッケージ化すること。配布物全体を作成することに重点を置く。
    • 依存関係の扱い: 依存JARファイルをそのまま(または展開して)アセンブリ内の特定のディレクトリに配置する。ライブラリのバイトコードやリソース自体は操作しない
    • 得意なこと:
      • 実行スクリプト、設定ファイル、ドキュメントなど、コード以外のファイルを含めた配布物を作成する。
      • ライブラリを lib ディレクトリにまとめるような、特定のディレクトリ構造を持つ配布物を作成する。
      • 複数の形式(ZIP, TAR.GZなど)で同じコンテンツをパッケージ化する。
      • ファイルモード(パーミッション)を設定する。
    • 苦手なこと:
      • 依存ライブラリに含まれるクラスやリソースの競合を解決したり、変換したりすること。
      • 特定のパッケージ名のクラスを別のパッケージ名に変更する(シェーディング)。
      • 単一の実行可能JAR(Uber JAR)作成において、複雑なマージ処理が必要な場合。
  • Maven Shade Plugin:

    • 目的: プロジェクトの成果物と依存ライブラリを単一の「Uber JAR」(または「Fat JAR」)に統合すること。実行可能な単一ファイルを作成することに重点を置く。
    • 依存関係の扱い: 依存JARファイルの内容(クラスファイル、リソースファイル)をすべて展開し、プロジェクト自身のクラスやリソースと一緒に単一のJARに再パックする。この過程で、クラスパスの競合を解決するための様々な機能(リソース変換、クラスのフィルタリング、パッケージ名の変更など)を提供する。
    • 得意なこと:
      • 依存ライブラリをすべて含んだ、配布しやすい単一の実行可能JARファイルを作成する。
      • 異なるJARに含まれるリソースファイル(例: META-INF/services, Spring BootのMETA-INF/spring.handlers など)をマージする。
      • 依存ライブラリのパッケージ名を変更して名前空間の競合を回避する(Shading)。
      • 不要なクラスやリソースを除外してJARのサイズを削減する。
    • 苦手なこと:
      • コード以外のファイル(実行スクリプト、設定ファイル、ドキュメントなど)を含めること。
      • ZIPやTAR.GZといったアーカイブ形式を作成すること(基本的にはJAR形式)。
      • 特定のディレクトリ構造を持つ配布物を作成すること。

使い分けの基準:

  • 依存ライブラリを含んだ単一の実行可能JARファイルを作成したい
    • 単純なケース(クラスパス競合が少ない、リソースマージが不要)であれば、Assembly Pluginの jar-with-dependencies + archive 設定でも可能です。
    • 複雑なケース(クラスパス競合が多い、リソースマージが必要、パッケージ名を変更したい)であれば、Shade Plugin が適しています。
  • 実行スクリプト、設定ファイル、ライブラリディレクトリなどを含む、特定のディレクトリ構造を持つ配布アーカイブ(ZIP, TAR.GZなど)を作成したい
    • Assembly Plugin が最も適しています。bin descriptor やカスタム descriptor を使用します。
  • ソースコードやドキュメントをまとめたアーカイブを作成したい
    • Assembly Plugin が最も適しています。src descriptor やカスタム descriptor を使用します。

多くの場合、Shade Pluginは単一のUber JARを作成するために使用され、Assembly Pluginはより広範な「アプリケーション配布物」を作成するために使用されます。両方のプラグインを同じプロジェクトで使用することも可能ですが、目的と出力形式に応じて適切に使い分けることが重要です。

まとめ

Maven Assembly Pluginは、プロジェクトのビルド成果物、依存関係、およびその他の必要なファイルを収集し、様々な形式の配布物(JAR, ZIP, TAR, ディレクトリなど)としてパッケージ化するための非常に強力で柔軟なツールです。

この記事では、Assembly Pluginの基本的な使い方、定義済み descriptor (jar-with-dependencies, bin, src) の利用方法、そして最も強力な機能であるカスタム descriptor の作成方法について詳細に解説しました。カスタム descriptor を使用することで、<fileSets>, <dependencySets>, <files> といった要素を組み合わせて、アプリケーションの構成に合わせた複雑な配布物構造を自由に定義できることを示しました。

また、<archive> 要素によるJARのマニフェスト設定、フィルタリング機能、ファイルモード設定、複数アセンブリの作成方法など、高度な設定についても触れました。最後に、よくある問題とそのトラブルシューティング、そして目的が似ているShade Pluginとの比較を通じて、それぞれのプラグインの得意なことや使い分けの基準を明確にしました。

Assembly Pluginを使いこなすことで、あなたのアプリケーションのビルドプロセスにおいて、配布物作成の自動化と標準化を実現できます。これにより、配布物の品質向上、開発者の手作業によるミス削減、そしてデプロイプロセスの効率化が期待できます。

ぜひ、あなたのプロジェクトでMaven Assembly Pluginを活用し、効率的で信頼性の高い配布プロセスを構築してください。不明な点があれば、Maven Assembly Pluginの公式ドキュメントも参照しながら、様々な設定を試してみることをお勧めします。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール