万人のためのオートメーション: Ivy による依存関係の管理

Apache Ant で共通リポジトリーを使って他のプロジェクトのソース・コードを共有する

プロジェクトやツール間にあるソース・コードの依存関係を管理するのは厄介な作業となりがちですが、必ずしもそう決めてかかる必要はありません。オートメーションのエキスパートである Paul Duvall が連載「万人のためのオートメーション」で今回説明するのは、大規模な Java™ プロジェクトであれば必ず必要になってくる無数の依存関係の管理に、Apache Ant プロジェクトの Ivy 依存関係管理ツールで対処する方法です。

Paul Duvall (paul.duvall@stelligent.com), CTO, Stelligent Incorporated

Paul DuvallPaul 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年) の著作にも貢献しました。



2008年 5月 06日

事実上、どのソフトウェア開発プロジェクトでも他のプロジェクトのソース・コードに依存せざるを得ません。例えば、プロジェクトの多くは log4j などのロギング・ユーティリティー、あるいは Struts などの Web フレームワークに依存していることでしょう。開発チームは依存するプロジェクトのソース・コードを保守することはしませんが、それでもその API に頼って自分たちのプロジェクトのカスタム・ソフトウェアを実装します。ソフトウェアが依存する他のプロジェクトの数、それに加えてこれらのプロジェクト自体が持つ依存関係が多くなればなるほど、ソフトウェアをビルドする作業は複雑になってきます。

この連載について

私たちは開発者として、ユーザーのプロセスを自動化するために作業しますが、自分自身の開発プロセスを自動化するチャンスは見落としがちです。そこで、連載記事「万人のためのオートメーション」では、実用的なソフトウェア開発プロセスの自動化を検討し、自動化を上手に適用するタイミングと方法を教えます。

これまで私は、以下のような不完全な手法でこのジレンマを解決しようとしているチームを目にしてきました。

  • すべての従属プロジェクト (JAR ファイル) を、プロジェクトのバージョン管理リポジトリーにチェックインされるディレクトリーに配置するという手法。この手法はリポジトリーを不必要に膨れ上がらせ、バージョンの違いを管理しにくくします。
  • 従属 JAR を共通ファイル・サーバーに配置するという手法。しかしこれではチームがバージョンの変更を管理できなくなります。
  • JAR ファイルを各開発者のワークステーション上にある特定のロケーションに手動でコピーするという手法。この手法では足りないファイルが判断しにくくなったり、バージョンの修正が困難になります。
  • 手動で、または自動ビルドの一環として HTTP の Get を実行し、ファイルを開発者のワークステーションにダウンロードするという手法。この手法ではスクリプトレットを複製しなければならなくなるため、管理されていない JAR ファイルが出てくる原因となりがちです。

私が従事したある中規模のプロジェクトには、1,000 の Java クラスと 100 を越える従属 JAR ファイルが含まれていました (私たちは上記で最初に挙げた不完全な手法を採用し、この大量の JAR ファイルのそれぞれをプロジェクトのバージョン管理リポジトリーにチェックインしていました)。図 1 に、このようなプロジェクトで見ることができる依存関係のタイプのほんの一部を示します。

図 1. Web 開発プロジェクトにおける JAR の依存関係の例
Web 開発プロジェクトにおける JAR の依存関係の例

推移的依存関係への固定化

推移的依存関係とは大げさな言い回しですが、これは Ivy が提供する単純ながらも強力な機能を表します。JAR ファイルのなかには、正常な動作をするために、さらに別の JAR ファイルに依存しているものもあります。Ivy では、コンポーネントの依存関係を宣言するのは一度だけです。その時点からは、基礎となる JAR ファイル依存関係のすべてを把握する必要はなくなり、プロジェクトでメインとなる JAR ファイルさえ知っていればよいことになります。ドキュメンテーションやコードを調べて依存関係を手作業で追いかけることに苦労した経験があるなら、この機能 1 つだけでも、プロジェクトで Ivy を構成するために時間を割く価値があると思うはずです。詳しくは、この記事の「依存関係に依存する」を参照してください。

図 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:settingsivy: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 のインストール方法

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 からです。taskdefivy 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 モジュールのディレクトリー構造
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 モジュールでは asmjakarta-orolog4j の 3 つの従属モジュールが定義されており (リスト 5 を参照)、asm モジュールには asm-tree というただ一つの従属モジュールがあるということです (リスト 6 を参照)。

図 3 に示す asm-tree のディレクトリー構造が、図 2 に示した asm モジュールのディレクトリー構造といかに似ているかを見てください。

図 3. asm-tree モジュールのディレクトリー構造
asm-tree モジュールのディレクトリー構造

違いはもちろん、この JAR ファイルには異なるクラスが含まれているという点で、図 2 に示した ivy.xml ファイルでの定義は asm-tree モジュールについて記述しています (偶然にも、asm-tree モジュールはその ivy.xml ファイルに依存関係を 1 つも定義していません)。


Ivy を使って拡張する

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 レポート
プロジェクトの依存関係を示す HTML レポート

その他のタスク

Ivy には他にも、Maven 用 POM ファイルの生成からローカル・ファイルシステム・キャッシュの消去に至るまで、さまざまな Ant タスクが用意されています。表 1 にそのうちの一部として、Ivy の Ant タスクとその目的を記載します。

表 1. その他の Ivy Ant タスク
タスク目的
settingsリポジトリーを保有するホストを認証する際に極めて便利です。
cachepathダウンロードされたファイルがローカル・ファイルシステム上でホストされる場所となるデフォルト・キャッシュのパスを上書きします。
repreportリポジトリーに含まれる複数のモジュールを対象にレポートを生成します。
installモジュールとそのすべての依存関係をインストールします。
makepomivy.xml ファイルから、Maven で使用できる pom.xml ファイルを作成します。
cleancacheローカル・ファイルシステムのキャッシュを消去して、次のビルドで JAR ファイルがリポジトリーから取得されるようにします。

Ivy に用意されたその他の Ant タスクについて調べるには、「参考文献」を参照してください。


すべては状況次第です

バイナリーのバージョン管理

Ivy によって JAR ファイルのバージョン管理を行う必要がなくなるというわけではありません。私が見てきたなかで、HTTP でアクセス可能なリポジトリーを与えられたチームが、バージョン管理システムにファイルを置くのをすっかり忘れてしまうという例は数多くあります。今から 1 年後にソフトウェアを作り直さなければならなくなった場合、HTTP リポジトリーが一元管理されていなければ、その作業は困難を極めるに違いありません。Subversion のような HTTP でアクセス可能なバージョン管理リポジトリーを使用すれば、一元管理が可能になるだけでなく、HTTP でアクセスできるため、負担は軽減します。

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 プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。

製品や技術を入手するために

  • Ant: Ant をダウンロードして、わかりやすく繰り返し可能な形でソフトウェアのビルドを開始してください。
  • Ivy: Ivy をダウンロードしてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Open source
ArticleID=313055
ArticleTitle=万人のためのオートメーション: Ivy による依存関係の管理
publish-date=05062008