garbagetown

個人の日記です

maven assembly plugin

シゴトでちょっとしたツールを書くことがたまにあるのですが、Java の場合、書くより動かす方が面倒くさい。
手元の eclipse では元気にサクサク動いていたコードが、いざ solaris にアップした途端 NoClassDefFoundError とか ClassNotFoundException でガンガン落ちるのはよくある話で。
その度にクラスパスがどうのライブラリがどうのとシェルを書く人生にもいい加減、嫌気が差してきたので、ちょっと調べてみました。

mvn archetype:create

まあとりあえず mvn archetype:create でプロジェクト作成。eclipse の新規プロジェクト作成ウィザードとか使っちゃダメ。絶対。

D:\work>mvn archetype:create -DgroupId=org.garbagetown -DartifactId=test
[INFO] Scanning for projects...
(snip)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13 seconds
[INFO] Finished at: Thu Nov 05 21:44:05 JST 2009
[INFO] Final Memory: 8M/14M
[INFO] ------------------------------------------------------------------------
D:\work>dir
(snip)
2009/11/05  21:44    <DIR>          .
2009/11/05  21:44    <DIR>          ..
2009/11/05  21:44    <DIR>          test

一行コマンド叩くだけでプロジェクトのディレクトリ構成を作ってくれる。便利。

D:\work>cd test
D:\work\test>tree /F
(snip)
D:.
│  pom.xml
│
└─src
    ├─main
    │  └─java
    │      └─org
    │          └─garbagetown
    │                  App.java
    │
    └─test
        └─java
            └─org
                └─garbagetown
                        AppTest.java

mvn eclipse:m2eclipse

このままでは eclipse に取り込めないので、mvn eclipse:m2eclipse を叩いてやる。mvn eclipse:eclipse との違いは こちら に詳しく書かれています。

D:\work\test>mvn eclipse:m2eclipse
[INFO] Scanning for projects...
(snip)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8 seconds
[INFO] Finished at: Thu Nov 05 21:51:18 JST 2009
[INFO] Final Memory: 7M/14M
[INFO] ------------------------------------------------------------------------

eclipse 用の .classpath やら .project ファイルやらができる。便利。

D:\work\test>tree /F
(snip)
D:.
│  .classpath
│  .project
│  pom.xml
│
└─src
    ├─main
    │  └─java
    │      └─org
    │          └─garbagetown
    │                  App.java
    │
    └─test
        └─java
            └─org
                └─garbagetown
                        AppTest.java

あとは eclipse に取り込んでガシガシ書いていけば良し。ライブラリ追加したくなったら pom.xml を右クリで Maven -> Add Dependency で検索して追加して終わり。依存性の連鎖も勝手に解決してくれれます(要 m2eclipse プラグイン)。

mvn assembly:assembly

で、ここからが本題。
先にも書いたように、ちまちまクラスパスを指定するようなシェルはもう書きたくない。依存ライブラリもまとめて固めて実行したい。
まさにこれを実現するのが maven-assembly-plugin です。このプラグインを使うためにちょっと手間がかかりますが、その恩恵を考えたら微々たるものなので、張り切って設定しましょう。
まずは pom.xml にプラグインの設定を追記。

(snip)
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <mainClass>org.garbagetown.test.Main</mainClass>
          </manifest>
        </archive>
      </configuration>
    </plugin>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <descriptors>
          <descriptor>src/main/assembly/distribution.xml</descriptor>
        </descriptors>
      </configuration>
    </plugin>
  </plugins>
</build>
<dependencies>
(snip)

maven-jar-plugin でマニフェストファイルに起動するメインクラスを指定したら、maven-assembly-plugin を設定ファイルの場所と共に指定します。
で、この設定ファイルはこんなんでいいらしい。こいつを src/main/assembly/distribution.xml として作成。

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
  <id>distribution</id>
  <formats>
    <format>zip</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <unpack>true</unpack>
      <scope>runtime</scope>
      <outputDirectory>/</outputDirectory>
    </dependencySet>
  </dependencySets>
</assembly>

あとは mvn assembly:assembly を実行すれば、自分で書いたコードと依存ライブラリの内容を全部一緒くたにまとめた zip ファイルが target ディレクトリに作成されます。

D:\work\test>mvn assembly:assembly
[INFO] Scanning for projects...
(snip)
[INFO] Building zip: D:\work\test\target\test-1.0-SNAPSHOT-distribution.zip
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17 seconds
[INFO] Finished at: Thu Nov 05 23:45:01 JST 2009
[INFO] Final Memory: 14M/26M
[INFO] ------------------------------------------------------------------------
D:\work\test>dir target
(snip)
2009/11/05  23:44    <DIR>          .
2009/11/05  23:44    <DIR>          ..
(snip)
2009/11/05  23:45         1,985,707 test-1.0-SNAPSHOT-distribution.zip
2009/11/05  23:43             2,448 test-1.0-SNAPSHOT.jar
(snip)

拡張子は zip だけど内容は jar と一緒だし、マニフェストファイルでメインクラスも指定しているので java -jar で実行できます。

D:\work\test\target>java -jar test-1.0-SNAPSHOT-distribution.zip

イカす!

付録

log4j.properties などの src/main/resources 配下のリソースもパッケージにまとめる場合は pom.xml に resource を指定する。
Java 5.0 を使う場合と UTF-8 で書く場合も pom.xml に追記する。

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
    </resource>
  </resources>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
        <encoding>utf-8</encoding>
      </configuration>
    </plugin>
(snip)

ちょっとメンドい。