事実上、どのソフトウェア開発プロジェクトでも他のプロジェクトのソース・コードに依存せざるを得ません。例えば、プロジェクトの多くは log4j などのロギング・ユーティリティー、あるいは Struts などの Web フレームワークに依存していることでしょう。開発チームは依存するプロジェクトのソース・コードを保守することはしませんが、それでもその API に頼って自分たちのプロジェクトのカスタム・ソフトウェアを実装します。ソフトウェアが依存する他のプロジェクトの数、それに加えてこれらのプロジェクト自体が持つ依存関係が多くなればなるほど、ソフトウェアをビルドする作業は複雑になってきます。
これまで私は、以下のような不完全な手法でこのジレンマを解決しようとしているチームを目にしてきました。
- すべての従属プロジェクト (JAR ファイル) を、プロジェクトのバージョン管理リポジトリーにチェックインされるディレクトリーに配置するという手法。この手法はリポジトリーを不必要に膨れ上がらせ、バージョンの違いを管理しにくくします。
- 従属 JAR を共通ファイル・サーバーに配置するという手法。しかしこれではチームがバージョンの変更を管理できなくなります。
- JAR ファイルを各開発者のワークステーション上にある特定のロケーションに手動でコピーするという手法。この手法では足りないファイルが判断しにくくなったり、バージョンの修正が困難になります。
- 手動で、または自動ビルドの一環として HTTP の Get を実行し、ファイルを開発者のワークステーションにダウンロードするという手法。この手法ではスクリプトレットを複製しなければならなくなるため、管理されていない JAR ファイルが出てくる原因となりがちです。
私が従事したある中規模のプロジェクトには、1,000 の Java クラスと 100 を越える従属 JAR ファイルが含まれていました (私たちは上記で最初に挙げた不完全な手法を採用し、この大量の JAR ファイルのそれぞれをプロジェクトのバージョン管理リポジトリーにチェックインしていました)。図 1 に、このようなプロジェクトで見ることができる依存関係のタイプのほんの一部を示します。
図 1. Web 開発プロジェクトにおける JAR の依存関係の例
図 1 からわかるように、Brewery プロジェクトのソース・コードは Hibernate、Struts 2、MySQL Connector、Cobertura に依存し、Cobertura は asm-2.2.1.jar、jakarta-oro-2.0.8.jar、og4j-1.2.9.jar といった別の JAR に依存し、さらに asm-2.2.1.jar は asm-tree-2.2.1.jar に依存しています。これは、考え得るネスト型依存関係の単純な一例に過ぎません。このなかの JAR バージョンが 1 つでも誤っていれば、コンパイル・エラーや予期しない振る舞いなど、トラブルシューシューティングするのに困難な問題が発生する可能性があります。
Java 開発者たちの興味を引くようになったのは、Apache Maven という、ビルドおよびプロジェクト管理ツールです。Maven は、一般に利用可能な Web サーバー (ibiblio) によってアクセスできる JAR ファイルの共通リポジトリーという概念を取り入れています。この Maven 方式はバージョン管理リポジトリーの大部分を使い果たしていた JAR ファイルの膨張を抑えますが、Maven を使用することによって、その「Convention over Configuration (設定より規約)」方式をソフトウェアのビルドにも適用しがちになり、ビルド・スクリプトをカスタマイズする際の柔軟性が制限されることになります。
ここで想像してみてください。Apache Ant を長年使用しているとしたら、共通リポジトリーを使用することによるメリットをどのようにして得られると思いますか?それには Maven のビルド方式を受け入れるしかないのでしょうか?幸いなことに、その必要はありません。これは Ant サブプロジェクトの 1 つ、Apache Ivy というツールのおかげです。Ivy では、より一貫性に優れ、繰り返し可能で、しかも保守もしやすい方法でプロジェクトのビルド依存関係すべてを管理することができます (Maven と Ivy の比較については、「参考文献」を参照してください)。この記事では、依存関係を管理する Ivy の基本的なインストールおよび構成方法を説明するとともに、その他にも詳細を検討できる追加情報を紹介します。
Ivy の開始手順は、2 つの Ivy 固有のファイルを作成し、いくつか Ant ターゲットを追加するだけの単純な作業です。2 つの Ivy 固有のファイルとして作成するのは、ivy.xml と Ivy 設定ファイルです。ivy.xml ファイルにはプロジェクトのすべての依存関係を記載し、ivysettings.xml (設定ファイルには任意の名前を指定することができます) にはリポジトリーを構成します。このファイルで構成したリポジトリーから、従属 JAR ファイルがダウンロードされることになります。
リスト 1 に単純な Ant スクリプトを記載します。このスクリプトは 2 つの Ivy タスク、ivy:settings と ivy:retrieve を呼び出します。
リスト 1. Ivy を使用した単純な Ant スクリプト
<target name="init-ivy" depends="download-ivy">
<ivy:settings file="${basedir}/ivysettings.xml" />
<ivy:retrieve />
</target>
|
リスト 1 では、ivy:settings が Ivy 設定ファイルを定義し、ivy:retrieve の呼び出しによって ivy.xml に宣言されたリポジトリーのうちの 1 つから JAR ファイルを取得します。
Ivy をダウンロードして使用する方法はいくつかあります。そのうちの 1 つは、手動で Ivy JAR ファイルを Ant lib ディレクトリー、または Ant スクリプトのクラスパスに定義した別のディレクトリーにダウンロードするという方法です。しかし私はオートメーションのファンなので、自動的に Ivy の JAR をダウンロードして Ant ターゲットにクラスパスを構成するという方法を選びます。リスト 2 に示すのは、この方法の一例です。
リスト 2. Ant を使用して Ivy を自動的にインストールする方法
<?xml version="1.0" encoding="iso-8859-1"?>
<project name="test-ivy" default="init-ivy" basedir="."
xmlns:ivy="antlib:org.apache.ivy.ant" xmlns="antlib:org.apache.tools.ant">
<property name="ivy.install.version" value="2.0.0-beta2" />
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpath="${ivy.jar.dir}/ivy.jar"/>
<target name="download-ivy">
<mkdir dir="${ivy.jar.dir}"/>
<get src="http://www.integratebutton.com/repo/
${ivy.install.version}/ivy-2.0.0-beta2.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>
</project>
|
リスト 2 の 2 行目が XML 名前空間を定義します。antlib は ivy.jar ファイル内にある antlib.xml というファイルを参照し、xmlns のその他の部分は ivy Ant タスク用の完全修飾パスを示しています。ivy.jar ファイルがダウンロードされるのは、ivy.home の値として設定された ${user.home}/.ant からです。taskdef は ivy Ant タスクを定義し、そのクラスパス・ロケーションを参照します。download-ivy ターゲットが ivy-2.0.0-beta2.jar をダウンロードし、その名前を dest 属性を使用して変更します。
Ivy のダウンロードと構成を完了すると、Ivy Ant タスク (リスト 1 で呼び出した 2 つのタスクなど) をどれでも使用できるようになります。
必要となるのは、プロジェクトのすべての従属 JAR を定義した ivy.xml ファイルです。リスト 3 にその一例を示します。
リスト 3. ivy.xml での依存関係の定義
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="./config/ivy/ivy-doc.xsl"?>
<ivy-module version="1.0">
<info organisation="com" module="integratebutton" />
<dependencies>
<dependency name="hsqldb" rev="1.8.0.7" />
<dependency name="pmd" rev="2.0" />
<dependency name="cobertura" rev="1.9"/>
<dependency name="checkstyle" rev="4.1" />
<dependency name="junitperf" rev="1.9.1" />
<dependency name="junit" rev="3.8.1" />
</dependencies>
</ivy-module>
|
リスト 3 にはファイル・ロケーションも、URL も指定されていないことに注目してください。そのため依存関係のリストを変更しなくても、異なるリポジトリー・ロケーションに移動できるようになっています。info 要素の organisation 属性は組織のタイプ (.net、.org、.com など) を識別します。その後に続く module が表しているのはモジュール名です。このモジュールの依存関係のリストが従う命名規則は次のリストで一層明確になりますが、とりあえずは、dependency name="cobertura" rev="1.9" は cobertura-1.9.jar に変換されることだけ覚えておいてください。
リスト 4 は Ivy 設定ファイルの一例です。この例では、リスト 3 の ivy.xml ファイルで使用しているリポジトリー・ロケーションおよび関連パターンを定義しています。
リスト 4. Ivy 設定ファイル
<ivysettings>
<settings defaultResolver="chained"/>
<resolvers>
<chain name="chained" returnFirst="true">
<filesystem name="libraries">
<artifact pattern="${ivy.conf.dir}/repository/[artifact]-[revision].[type]" />
</filesystem>
<url name="integratebutton">
<artifact pattern="http://www.integratebutton.com/repo/[organisation]/[module]/
[revision]/[artifact]-[revision].[ext]" />
</url>
<ibiblio name="ibiblio" />
<url name="ibiblio-mirror">
<artifact pattern="http://mirrors.ibiblio.org/pub/mirrors/maven2/[organisation]/
[module]/[branch]/[revision]/[branch]-[revision].[ext]" />
</url>
</chain>
</resolvers>
</ivysettings>
|
リスト 4 では、filesystem 要素でローカル・ワークステーションでのロケーション・パターンを定義し、これに続く 2 つの url 要素のそれぞれで、JAR ファイルをダウンロードできるロケーションを定義しています。最初に定義しているのは、私が管理する integratebutton.com にあるカスタム・リポジトリーです。次に、オープンソースの JAR ファイルが多数含まれる (私が管理していない) 外部 Maven リポジトリーを定義しています。Ivy は最初のリポジトリーからファイルをダウンロードできない場合 (例えばリポジトリーがダウンしていたり、ファイルが指定されたロケーションにない場合) には、次のリポジトリーでダウンロードを試行します。Ivy の優れた点は、いったん JAR をダウンロードすると、ローカル・ファイルシステムにそのファイルを配置することです。そのため、Ivy ではビルドのたびにファイルをダウンロードする必要がありません。
あるモジュールが他のモジュールとの依存関係を持つことは何も珍しいことではありません。図 1 では、例えば cobertura-1.9.jar ファイルには複数の依存関係があり、その依存関係の 1 つとなっている asm-2.2.1.jar には asm-tree-2.2.1.jar との依存関係がありました。Ivy のようなツールを使用しないのであれば、これらの JAR の正しいバージョンがクラスパスに含まれていること、そして JAR バージョン間にまったく矛盾がないことを自分で確認しなければなりません。一方、Ivy を使えば cobertura モジュールとそのすべての従属モジュールを定義するだけで済みます (リスト 5 の ivy.xml ファイルの例を参照)。この ivy.xml ファイルは、cobertura-1.9.jar ファイルと同じディレクトリーに配置されている点に注意してください。
リスト 5. ivy.xml での依存関係の定義
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="cobertura" module="cobertura" revision="1.9"/>
<configurations>
<conf name="master"/>
</configurations>
<publications>
<artifact name="cobertura" type="jar" conf="master" />
</publications>
<dependencies>
<dependency org="objectweb" name="asm" rev="2.2.1" conf="master"/>
<dependency org="jakarta" name="oro" rev="2.0.8" conf="master"/>
<dependency org="apache" name="log4j" rev="1.2.9" conf="master"/>
</dependencies>
</ivy-module>
|
リスト 5 で強調表示している依存関係は、objectweb org とその名前 asm を、使用する特定のリビジョンと併せて定義しています。Ivy はこの情報と ivysettings.xml のリポジトリー定義 (リスト 4 を参照) を使用して、JAR ファイル依存関係をダウンロードします。
図 2 に、リスト 4 の ivysettings.xml ファイルでの構成に準拠したリポジトリーのディレクトリー構造を示します。
図 2. asm モジュールのディレクトリー構造
図 2 には、asm の依存関係を定義する ivy.xml ファイル (リスト 6 で説明) も示されていることに注目してください。リスト 6 の asm モジュール用 ivy.xml のスニペットが、このモジュールが持つただ一つの依存関係、asm-tree-2.2.1.jar を示しています。
リスト 6. asm の依存関係を定義する ivy.xml
... <dependencies> <dependency org="objectweb" name="asm-tree" rev="2.2.1" conf="master"/> </dependencies> ... |
要するに、cobertura モジュールでは asm、jakarta-oro、log4j の 3 つの従属モジュールが定義されており (リスト 5 を参照)、asm モジュールには asm-tree というただ一つの従属モジュールがあるということです (リスト 6 を参照)。
図 3 に示す asm-tree のディレクトリー構造が、図 2 に示した asm モジュールのディレクトリー構造といかに似ているかを見てください。
図 3. asm-tree モジュールのディレクトリー構造
違いはもちろん、この JAR ファイルには異なるクラスが含まれているという点で、図 2 に示した ivy.xml ファイルでの定義は asm-tree モジュールについて記述しています (偶然にも、asm-tree モジュールはその ivy.xml ファイルに依存関係を 1 つも定義していません)。
Ivy の基本的な使い方がわかったところで、その他の便利な Ant タスクをいくつか紹介します。
Ivy には、プロジェクトの従属ファイルに関するレポート作成タスクが用意されています。Ivy の report という Ant タスクを呼び出して依存関係のリストを作成する方法は、リスト 7 のとおりです。
リスト 7. Ant から Ivy 依存関係レポートを生成する方法
<target name="ivy-report" depends="init-ivy">
<ivy:report todir="${target.dir}/reports/ivy"/>
</target>
|
リスト 7 のスクリプトによって生成される HTML レポートには、プロジェクトの従属ファイルのリストが表示されます。図 4 に、このレポートを示します。
図 4. プロジェクトの依存関係を示す HTML レポート
Ivy には他にも、Maven 用 POM ファイルの生成からローカル・ファイルシステム・キャッシュの消去に至るまで、さまざまな Ant タスクが用意されています。表 1 にそのうちの一部として、Ivy の Ant タスクとその目的を記載します。
表 1. その他の Ivy Ant タスク
| タスク | 目的 |
|---|---|
settings
| リポジトリーを保有するホストを認証する際に極めて便利です。 |
cachepath
| ダウンロードされたファイルがローカル・ファイルシステム上でホストされる場所となるデフォルト・キャッシュのパスを上書きします。 |
repreport
| リポジトリーに含まれる複数のモジュールを対象にレポートを生成します。 |
install
| モジュールとそのすべての依存関係をインストールします。 |
makepom
| ivy.xml ファイルから、Maven で使用できる pom.xml ファイルを作成します。 |
cleancache
| ローカル・ファイルシステムのキャッシュを消去して、次のビルドで JAR ファイルがリポジトリーから取得されるようにします。 |
Ivy に用意されたその他の Ant タスクについて調べるには、「参考文献」を参照してください。
Ivy は従属ファイルを一箇所に集め、開発チームが JAR ファイルをバージョン管理リポジトリー間でコピーする際に起こりがちなリポジトリーの膨張を抑えます。単純なプロジェクトを動かしているのであれば、JAR ファイルをバージョン管理システムにチェックインしたり、この記事の冒頭に記載したような手法を使っても、それほど手間はかからないでしょう。しかしプロジェクトの規模が大きくなったり、あるいはファイルを共有するエンタープライズ環境で作業している場合には、共通の手段が必要となります。いずれの場合にしても、Ivy はプロジェクト依存関係の定義をより一貫した取り掛かりやすい作業にするので、プロジェクトで Ivy の使用を検討するだけの価値はあります。
学ぶために
-
Apache Ivy: Ivy プロジェクトのサイトにアクセスして、マニュアル、チュートリアル、コミュニティーのリソースを入手してください。
- 「Ivy in 4.2 steps」(Andrew Glover 著、testearly.com、2007年6月): 簡単な手順で Ivy の実行を開始できます。
-
Ivy / Maven2 Comparison (Apache Ant Ivy Project): Ivy と Maven 2 の依存関係管理について比較検討しています。
- 『Ant in Action』(Steven Loughran、Erik Hatcher 共著、Manning、2007年): この優れた著書の第 11 章では、Ivy を使用した依存関係管理に話題を絞っています。
- 「万人のためのオートメーション」(Paul Duvall、developerWorks): この連載のすべての記事を読んでください。
-
テクノロジー・ブックストアで、この記事で取り上げた技術やその他の技術に関する本を探してください。
-
developerWorks Java technology ゾーン: Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。
製品や技術を入手するために
議論するために
-
Improve Your Java Code Quality ディスカッション・フォーラム: このディスカッション・フォーラムでは、developerWorks の常連寄稿者 Andrew Glover がコード品質の改善を重点に、コンサルタントとして豊富な専門的知識を提供しています。
-
Accelerate development space: developerWorks の寄稿者としてお馴染みの Andrew Glover がホストを務める、開発者テスト、継続的インテグレーション、コード・メトリック、そしてリファクタリングに関するあらゆるものを網羅したワンストップ・ポータルです。
-
developerWorks blogs から developerWorks コミュニティーに加わってください。

Paul Duvall は、Stelligent Incorporated の最高技術責任者です。同社はアジャイル・コンサルタント会社として、開発チームが production-ready software を提供できるように支援しています。彼は Addison-Wesley Signature Series から出版されている『Continuous Integration: Improving Software Quality and Reducing Risk』(Addison-Wesley Professional、2007年、2008年度 Jolt Award を受賞) の共著者で、『UML 2 Toolkit』(Wiley、2003年) および『No Fluff Just Stuff Anthology』(Pragmatic Programmers、2007年) の著作にも貢献しました。