nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

MavenではじめるJava EE

JBoss / WildFly (全部俺) Advent Calendar 2013の2日目です。ひとつJBoss製品のパッチバイナリをビルドしてテストチームに渡す段取りを終えて一息ついたところです。

Java EE Advent Calendar 2013というのもあるのですが今年も埋まったようで、楽しみです。今日はこちらもJBoss特化ではなくJava EE寄りの話題で。

Java EEのコードをMavenでビルドするときには<dependency>を定義するわけですが、記述方法をGoogle検索すると結構バリエーションが出てきたりして困ったりすることがあります。以下の例ではJava EE 6 にしていますが7でも一緒です。

一番シンプルなのはjavax:javaee-apiを使うものです。

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>6.0</version>
  <scope>provided</scope>
</dependency>

Webプロファイル用のjavax:javaee-web-apiというのもあります。

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-web-api</artifactId>
  <version>6.0</version>
  <scope>provided</scope>
</dependency>

シンプルでとても良い。と思うかもしれませんがこの依存には実際には罠があります。単体テストを行おうとすると"java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file"とか出力されて単体テストが失敗することがあります。実はこれらの依存で取得できるjarは、全クラスのメソッドボディが削除されている加工されたクラスファイルが格納されています。APIとしての参照、つまりコンパイル時の利用は何も問題はないのですが、実行はできないのです。単体テストなどで実行するには実装側、つまりアプリケーションサーバで提供されるjarファイルが必要になります。知らずにこの依存を採用して途中で地雷踏んだりしないように、踏んでしまって二度手間にならないように、きちんとしたJava EEプロジェクトを作りたい場合は最初から実装サイドで提供されているjarを使うというのが正しいアプローチです。

ちなみに上記のFullとWebのAPI jarはメソッドボディが削られているのでサイズにほぼ差がなく、979KBと930KBです。

さて、JBossで提供しているJava EE APIのjar群はBOM(Bill of materials)でまとめられています。BOMは一連の依存とそのバージョンを定義したもので、いろいろライブラリがあって取捨選択して使うような状況で何のどのバージョン使えばいいの?バージョンの組み合わせは?などと迷わなくて済むように、依存をひとまとまりに定義した特殊なPOMのことです。

BOMを使うにはまず<dependencyManagement>を定義します。バージョンはhttp://search.maven.org/で検索して一番新しいやつを使えば良いです。この例ではWebプロファイルを利用しています。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.spec</groupId>
      <artifactId>jboss-javaee-web-6.0</artifactId>
      <version>3.0.2.Final</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencyManagement>を宣言するだけではプロジェクトの依存関係には何も影響はしません。次に実際の<dependency>を記述していきますが、記述する依存はBOMに書いてあるので必要なものをコピーすれば良いです。もちろん最小の依存が定義されているというのが理想な状態ではあるわけですが、面倒だったらとりあえず全部コピーしてしまっても良いと思います。<dependency>を定義するとき、バージョンはBOMで管理されているので記述する必要はありませんし、依存管理を破壊してしまうため基本的には記述してはいけません。Java EEAPIの依存はほぼ<scope>provided</scope>になると思いますが、それはpom.xmlで明記する必要があります。

<dependency>
  <groupId>javax.inject</groupId>
  <artifactId>javax.inject</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.enterprise</groupId>
  <artifactId>cdi-api</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.jboss.spec.javax.ws.rs</groupId>
  <artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
  <scope>provided</scope>
</dependency>

MavenリポジトリにはグループIDがorg.jboss.javaeeのものやorg.jboss.spec:jboss-javaee_6.0_specという名前が似ている紛らわしいものも見つかると思いますが、これはBOMになる前の古いものなので無視してください。