Apache Ivyを触ってみました。 - Home | Apache Ivy(TM) -- http://ant.apache.org/ivy/index.html 入門編としては以下のBlogが大変参考になりました: - Apache Ivyの紹介と基本的な使い方 - 達人プログラマーを目指して -- http://d.hatena.ne.jp/ryoasai/20110101/1293904479 Apache Ivyは依存関係のjarを取得する点に専念しており、その分Mavenのような複雑さはありません。Antが使えるのであればすんなりと入っていけると思います。Antならではの細かいカスタマイズに応えてくれます。その点で、Mavenを適用できないレガシーなプロジェクトであったり、依存関係の処理にMavenでは対応しきれない特殊な部分が出てくるようなプロジェクトには最適です。 今回試したのは、「MavenのローカルリポジトリをIvyから参照する」です。 #more|| * Ivyの"cache"ディレクトリについて 実は当初勘違いしていて、Ivyって依存関係を".ivy"とかにDLして、そこに全部jarを保存しておいてくれるものかと思ってました。Mavenの".m2/repository/"みたいな感じで、classpathは適当にAntのbuild.xml中で自動解決してくれる~みたいな。 しかしこれは完全な勘違いで、Ivyのメインはあくまでも依存関係を「ダウンロード」することにあります。classpathの調整はやってくれないんですね。じゃあどう使うのかというと、Antプロジェクトの中に"lib"ディレクトリを作っておいて、""を実行することでその中にjarをダウンロードしてくれます。 ですが、複数のいろいろなプロジェクトで""するたびに毎回リポジトリからDLするのは通信の無駄が発生します。 というわけで、Ivyは一旦"$HOME/.ivy2/cache/"ディレクトリ以下にjarなどのDLファイルをキャッシュします。 ""でプロジェクトローカルの"lib/"には、まずこのキャッシュを見に行って、無ければMavenなどのリポジトリに取りに行くようです。 逆に言えば、一旦、ひと通りのプロジェクトで""してしまえばキャッシュは不要です。ということで、ディスクスペースの節約のためにキャッシュをクリアしましょう・・・ということで""というタスクが提供されています。これを使うと "$HOME/.ivy2/cache/" 以下が空っぽになります。 * Mavenのローカルリポジトリとの連携について ここも勘違いしてたところがありまして、当初 "$HOME/.ivy2/cache/" って永続的なものかと思ってました。なので、「う~ん、Mavenも使ってたら "$HOME/.m2/repository/" と "$HOME/.ivy2/cache/" で重複してるので無駄だよな~、".ivy2/cache/" を ".m2/repository/" で使い回しできないかな?」と思ったんです。 ですが上記の通り ".ivy2/cache/" はあくまでもキャッシュであるため、いつかは "" でクリアされてしまうんです。 一応技術的には ".m2/repository/" を ".ivy2/cache/" に使い回しも出来るようなんですが・・・ ''しかしそんなことをしてしまうと、うっかり "" するとそれまでせっかく蓄積されたMavenのローカルリポジトリがゼロクリアされてしまう'' んですよね。いやまぁ、また自動でDLされるんだからいいじゃん、と言えばそうなんですが。 じゃぁどこでIvyとMavenのローカルリポジトリは連携するのか?ですが、単純にIvyが依存関係を探すリポジトリとして ".m2/repository/" を追加する、という連携方法になります。 あり得るパターンとしてはMavenで構築した自作ライブラリを"mvn install"で ".m2/repository/" 以下にインストールして、それをIvyで取りに行く、みたいな感じでしょうか。 ぶっちゃけ "" で ".ivy2/cache/" 以下をクリアしたとしても、プロジェクトローカルには ".jar" ファイルが置かれてしまうので、Mavenも使ってたらどっちにしてもjarファイルは重複して存在してしまいます。 しかしそもそもIvyを使う目的は「Mavenでは歯が立たない様な依存関係の解決」にあり、どうしてもそうなるとプロジェクトローカルに".jar"を置く、という流れになってしまうので、これはもう逃れられない訳です。それが嫌ならMavenにせーよ、という。 ちなみにプロジェクトローカルにjarをDLするというIvyの流れ上、依存対象のjarがアップデートされても自動的には同期してくれません。バージョン番号変えるとか、一旦 "" するとか、他にも更新チェック用の細かい設定があったりするようです。そのへんは今回は試してません。 参考: - Use maven repository as local ivy cache - Stack Overflow -- http://stackoverflow.com/questions/6216224/use-maven-repository-as-local-ivy-cache - java - Apache Ivy: Difference between local Ivy cache and local repository - Stack Overflow -- http://stackoverflow.com/questions/9366281/apache-ivy-difference-between-local-ivy-cache-and-local-repository - java - ivysettings.xml: add local maven path - Stack Overflow -- http://stackoverflow.com/questions/8617963/ivysettings-xml-add-local-maven-path * 実際に ".m2/repository/" にインストールしたjarをIvyでDLしてみる というわけで試してみました。 環境: Win7-SP1 64bit JDK 1.7.0_07, 64bit Oracle JVM Apache Maven 3.0.4 (r1232337; 2012-01-17 17:44:56+0900) Apache Ant(TM) version 1.8.2 compiled on December 20 2010 Apache Ivy 2.2.0 ** まずMavenでjarを作ります。 "mvnjartest"という名前でMavenプロジェクトを作ってみました。 commons-langに依存させます。 mvnjartest/pom.xml: #pre||> 4.0.0 mvntest mvnjartest jar 1.0-SNAPSHOT mvnjartest http://maven.apache.org commons-lang commons-lang 2.1 junit junit 3.8.1 test ||< ライブラリの動作としては、文字列をcapitalizeしたものを返します。 src/main/java/mvnjartest/App.java: #code|java|> package mvnjartest; import org.apache.commons.lang.WordUtils; public class App { String message; public App(String m) { this.message = m; } public String capitalize() { return WordUtils.capitalizeFully(message); } } ||< (testは省略) ビルドしてローカルリポジトリにインストールします。 > mvn install 以下のようなディレクトリ構成でインストールされました。 "$HOME/.m2/repository/ mvntest/ mvnjartest/ maven-metadata-local.xml 1.0-SNAPSHOT/ _maven.repositories maven-metadata-local.xml mvnjartest-1.0-SNAPSHOT.jar mvnjartest-1.0-SNAPSHOT.pom ** 上で作ったjarをIvyでDLしてくるサンプルアプリを作ります。 "mvnjartest-1.0-SNAPSHOT.jar"をIvyでDLしてくるサンプルを "resolv-test" というプロジェクト名で作ってみました。 まず依存性を定義する ivy.xml を用意します。 resolv-test/ivy.xml: #pre||> ||< 続いて、"$HOME/.m2/repository/" をリポジトリて参照する設定を ivysettigs.xml として用意します。 resolv-test/ivysettings.xml: #pre||> ||< Ivyでは依存性の解決方法でStrategyパターンを採用しており、ファイルシステム上から取得する方法が提供されています。解決方法(resolver)の連鎖(chain)に、"$HOME/.m2/repository/" 以下を参照するfilesystem resolverを組み込んでいます。filesystem上になければibiblioから取得するresolverを追加しています。 "ibiblio"というのは、インターネット上にあるリソースをコレクションしたミラーサイトの一種のようです。ibiblioが提供しているミラーの中に、Mavenリポジトリが含まれています。 ibiblioについて: - http://en.wikipedia.org/wiki/Ibiblio - http://www.ibiblio.org/ - http://mirrors.ibiblio.org/maven2/ -- Maven2のリポジトリ構成になってました。 肝心のbuild.xmlです。build.xml中にjavaソースファイルも作成するように手抜きしてます。 resolv-test/build.xml: #pre||> package example; import mvnjartest.App; public class Hello { public static void main(String[] args) { App a = new App("hello world"); System.out.println(a.capitalize()); } } ||< antでデフォルトの"run"ターゲットを実行すると・・・ + "src/example/Hello.java"を生成して、 + "lib/" 以下にIvyで依存jarをDLして、 + コンパイルして、実行します。 "ant resolve" でIvyの依存性解決だけを単体で動かしてみた時の出力: #pre||> > ant resolve Buildfile: ...\resolv-test\build.xml resolve: [ivy:retrieve] :: Ivy 2.2.0 - 20100923230623 :: http://ant.apache.org/ivy/ :: [ivy:retrieve] :: loading settings :: file = ...\resolv-test\ivysettings.xml [ivy:retrieve] :: resolving dependencies :: ivytest#resolv-test;working@HOGEHOGE [ivy:retrieve] confs: [default] [ivy:retrieve] found commons-lang#commons-lang;2.0 in ibiblio ### ibiblioでcommons-langが見つかりました。 [ivy:retrieve] found mvntest#mvnjartest;1.0-SNAPSHOT in local-m2-repo [ivy:retrieve] found commons-lang#commons-lang;2.1 in local-m2-repo ### "$HOME/.m2/repository/" 上からmvnjartestとcommons-langが見つかりました。 ### commons-langについてはmvnjartestのビルド時点でDLしています。 [ivy:retrieve] downloading C:\Users\xxxx\.m2\repository\mvntest\mvnjartest\1.0-SNAPSHOT\mvnjartest-1.0-SNAPSHOT.jar... [ivy:retrieve] .. (1kB) [ivy:retrieve] [SUCCESSFUL ] mvntest#mvnjartest;1.0-SNAPSHOT!mvnjartest.jar (18ms) [ivy:retrieve] downloading C:\Users\xxxx\.m2\repository\commons-lang\commons-lang\2.1\commons-lang-2.1.jar ... [ivy:retrieve] ..... (202kB) [ivy:retrieve] .. (0kB) [ivy:retrieve] [SUCCESSFUL ] commons-lang#commons-lang;2.1!commons-lang.jar (103ms) ### 両方のjarファイルとも、ローカルからプロジェクトの"lib/"以下にコピーされました。 [ivy:retrieve] :: resolution report :: resolve 853ms :: artifacts dl 127ms [ivy:retrieve] :: evicted modules: [ivy:retrieve] commons-lang#commons-lang;2.0 by [commons-lang#commons-lang;2.1] in [default] --------------------------------------------------------------------- | | modules || artifacts | | conf | number| search|dwnlded|evicted|| number|dwnlded| --------------------------------------------------------------------- | default | 3 | 3 | 3 | 1 || 2 | 2 | --------------------------------------------------------------------- [ivy:retrieve] :: retrieving :: ivytest#resolv-test [ivy:retrieve] confs: [default] [ivy:retrieve] 2 artifacts copied, 0 already retrieved (204kB/21ms) BUILD SUCCESSFUL Total time: 1 second ||< この時点で、mvnjartestとcommons-langのjarファイルは3箇所に重複して存在することになります。 + "$HOME/.m2/repository/" 以下 + "$HOME/.ivy2/cache/" 以下 + "resolv-test/lib/" 以下 "$HOME/.ivy2/cache/"以下は、 ""をトリガーする以下のターゲットで空っぽにします。 > ant clean-cache これでjarファイルの重複は2箇所、Ivyの使用目的としては必要最低限になります。 もしもMavenを全く使用せず、開発マシン上にはAntとIvyのみがセットアップされているのであれば、"$HOME/.m2/repository/" も存在しないためjarファイルの重複は存在しなくなります。 ・・・というところまで理解してれば、GroovyのGrapeで"$HOME/.m2/repository/"との連携方法も整理できるかな・・・。 ** おまけ : ibiblio resolverだけでも"$HOME/.m2/repository/"を参照できました。 ivysettings.xml: #pre||> ||< もともと"m2compatible"が有効なので、rootで指定したリソース以下をそのままMaven2リポジトリとして参照できるようです。なので、あとはrootで"file:"以下をローカルファイルシステムを指定するだけで ibiblio resolverでも解決できてしまうようです。 参考: - Groovy - Grape, "Add your local Maven2 repository" -- http://groovy.codehaus.org/Grape 何気に次のエントリのネタにしようとしてた内容・・・。