Play2アプリをオフラインでビルドする

Play2はsbtを使用してビルドを行っており、必要なライブラリをインターネット経由でダウンロードできることが前提になっていますが、インターネットに接続できない環境でPlay2を使用したアプリケーションをビルドする必要があったのでやってみました。
なんとなくPlay2が使用しているIvyのローカルキャッシュをそのまま持っていけばビルドできるだろうと思っていたのですが、いくつかハマりポイントがあったのでメモも兼ねて手順を書いておきます。Play2だけではなくて普通にsbtを使ったプロジェクトにも応用できると思います。

前提知識と基本方針

まず、Play2はsbtを使用していますが、sbt(というかIvy)が標準で使用するUSER_HOME/.ivyではなくPLAY_HOME/repositoryをIvyのホームディレクトリとして使用します。同様にsbtのホームディレクトリもUSER_HOME/.sbtではなくPLAY_HOME/sbtを使用します。これらの設定はPLAY_HOME/framework/sbt/sbt.boot.propertiesに記述されています。
以上を踏まえてここで説明するオフラインでビルドできるようにする手順の基本的な方針は以下の通りです。

  • Play2アプリケーションをsbtでビルドできるようにする
  • Play2アプリケーションのプロジェクト内のディレクトリをIvyのホームディレクトリに設定する
  • この状態でsbtでビルドを行い、必要なjarをローカルキャッシュに格納する

こうやって作成したPlay2アプリケーションのソースツリーはオフライン環境でもビルドできるはずです。

Play2アプリをsbtでビルドできるようにする

まずはPlay2アプリケーションをsbtでビルドできるようにします。今回はPlay 2.0.4を使っているのですが、Play 2.0.4はsbt 0.11.3を使用しているのでsbt 0.11.3を実行する準備をしておきます。
ここでは以下のようにPlay2アプリのプロジェクトルートにsbtのランチャーのjarファイルやシェルスクリプトなどを配置し、プロジェクト内にリポジトリなどを作成することを想定します。sbtのランチャーはこのへんからダウンロードしてください。

/play2-app
  |
  +-/app
  |
  +-/test
  |
  +-/conf
  |
  +-/project
  |
  +-/repository(ローカルリポジトリ)
  |
  +-sbt.sh(sbtを実行するためのシェルスクリプト)
  |
  +-sbt-launch-0.11.3.jar(sbtのランチャー)
  |
  +-sbt.boot.properties(sbtの起動設定)

まずはPLAY_HOME/framework/sbt/sbt.boot.propertiesをコピーし、bootとivyセクションを以下のように書き換えたファイルを作成します。

[boot]
  directory: ./repository/sbt/boot

[ivy]
  ivy-home: ./repository/ivy

で、この設定ファイルを使用してsbtを起動するために以下のような感じのシェルスクリプトをsbt.shとして作成します。

#!/bin/sh
java -Dsbt.boot.properties=sbt.boot.properties -Xms1024M -Xmx2024M -Xss2M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M -jar sbt-launch-0.11.3.jar "$@"

ローカルキャッシュの作成

この状態で「sbt dist」などを実行するとrepostoryディレクトリ内にローカルキャッシュが作成されるはずです。repository/ivy/cacheディレクトリ直下にはライブラリ毎のディレクトリに加えていくつかのXMLファイルとpropertiesファイルも生成されていると思いますが、これらは削除してしまって構いません。
一応、予定ではこの状態でソースツリーごとオフライン環境にコピーすればビルドできる予定でした。が、どうしても以下の2つのライブラリだけはローカルキャッシュが存在するにも関わらずリモートリポジトリに取りに行ってしまいます。

  • jackson-core-asl
  • jackson-mapper-asl

これはPlay2が使用しているJSONライブラリJerksonが使用しているJava用のJSONライブラリで、Jerksonはこれらのライブラリのバージョンを「[1.9.0,2.0.0)」のように範囲指定しています。
そのため指定範囲内でより新しいバージョンが存在しないかを毎回チェックしに行ってしまうようです。sbtのドキュメントなどを見ると

offline := true

という設定をプロジェクトの設定に追加すればリモートリポジトリへの更新チェックを行わないようにできると書いてあるのですが、Play2アプリケーションのBuild.scalaにこの設定を追加しても変化ありませんでした。sbt側の問題なのか、Ivyの仕様なのかの切り分けはできていませんが、時間があればソースを追ってみたいところです。
仕方ないのでJacksonだけ、ローカルリポジトリに配置してやることにします。PLAY_HOME/repository/localにPlay2で使用しているライブラリが格納されているはずです。この中からorg.codehaus.jacksonディレクトリをrepository/ivy/localディレクトリにコピーします。これでオフラインでもビルドが通るようになるはずです。

Play2標準のライブラリだけ使うなら…

実際に試してはいないのですが、Play2標準のライブラリだけを使用したアプリケーションであればわざわざローカルキャッシュを作成しなくてもPLAY_HOME/repository/localをコピーするだけでいけるのではないかと思います。ただ、Play2以外のライブラリを使用する場合はjarファイルがローカルキャッシュに落ちてくるのでこのような方法を採るのが最も簡単だと思います。
sbt(というかIvy)はMavenと違ってローカルにキャッシュとリポジトリを持っており、ここが結構ハマりやすいポイントだったりするのですが、長くなってしまったのでそのあたりについては別のエントリに書きたいと思います。