ポータビリティのレベル
これらの基本ツールを押さえておけば、自分のJavaプロジェクトのポータビリティがどの程度のレベルに位置しているかを正しく判別することができます。Mavenでは、Javaポータビリティとそれに対応する問題点を次の4つのレベルで表現します。
- ワイド(Wide)
- インハウス(In-house)
- 環境的(Environmental)
- 非ポータブル(Non-portable)
ワイド(Wide)
Mavenの世界で「ポータビリティがワイドである」と言えば、誰でもそのプロジェクトのソースをダウンロードし、修正なしでコンパイルし、POMまたは標準Maven以外の要件にインストールできるということです。これが一番高いレベルのポータビリティです。同じプロジェクトをビルドするユーザーは、余計な作業を実行する必要がほとんどありません。このレベルのポータビリティは、特にオープンソースプロジェクトで重要視されます。オープンソースプロジェクトが成功するためには、コントリビュータになる可能性を持った人が簡単にプロジェクトをダウンロードしてインストールできるようにすることが必要だからです。
ご想像の通り、一番高いレベルのポータビリティは、手に入れることも一番困難です。ワイドなポータビリティを実現するためには、広く配布されているプロジェクトやツールであっても、ライセンスの問題が考えられるため、それに依存するわけにはいきません(この理由で、市販のソフトウェアパッケージのほとんどは利用不可になります)。また、Mavenの成果物として配布されるソフトウェアコンポーネントについても、同様の問題が考えられます。例えば、皆さんのプロジェクトでMySQLを利用している場合、ユーザーはプロジェクト本体に加えてMySQLもダウンロードしてインストールしなければなりません。これではポータビリティがワイドであるとは言えません(システムを変更せずに使用できるのはMySQLの既存ユーザーだけです)。一方、HSQLDBを使用している場合は、MavenのセントラルリポジトリからHSQLDBを入手できるので、ポータビリティはワイドとなります。
インハウス(In-house)
ほとんどの重要なソフトウェア開発では、インハウスのポータビリティがおそらく望むことのできる最高レベルのポータビリティです。オープンソースの開発チームにしろ、クローズドソースの製作会社にしろ、ソフトウェアプロジェクトの大多数はこのレベルに位置します。このレベルのポータビリティの中心にあるのが、選ばれたユーザーのみが内部(インハウス)のリモートリポジトリにアクセスできるというプロジェクト要件です。インハウスとは、必ずしも特定の会社を意味するのものではありません。幅広く普及しているオープンソースプロジェクトの中には、特定のツールや接続アクセスを必要とするものがあるかもしれません。このような場合はインハウスに分類されます(このレベルについての詳細は、「補足説明3 インハウスポータビリティの詳細」を参照してください)。
インハウスポータビリティ自体には何も問題がないので、プロジェクトの形を変えて、これを避けようとしないでください。プロジェクトを複数の相互依存関係のあるパーツに分割することに意味がある場合は、分割してかまいません。Mavenのセントラルリポジトリでは入手できないバージョンを使うことに意味がある場合は、使ってかまいません。プロジェクトのPOMのrepository要素を用いて独自のパブリックリポジトリと参照依存性を作成すれば、誰もが切望する「ワイドなポータビリティ」を実現することができます。
環境的(Environmental)
Mavenでは、コードとリソースの生成に主に関係する、環境的なポータビリティレベルのプロファイルが作成されます。例えば、本番データベースとは別のデータベースを使うテスト環境があるとします。本番データベースとテストデータベースが異なるため、テストマシンでビルドした場合は、少なくとも1つのリソースファイルを(または最悪の場合はコードも)修正しなければなりません。ファイルを変更しなければ正しくビルドして特定の環境で実行することができない場合、そのプロジェクトはよくても環境的なポータビリティしか実現していません。例えば、テスト環境内のテストデータベースへの参照と本番環境内の本番データベースへの参照を含むプロジェクトは、環境的なポータビリティを実現しています。ただしこのプロジェクトは、未定義でプロファイルも作成されていない別の環境に移動すると動作しなくなります。従って、定義された環境間でのみポータビリティがあると言えます。
本稿のサンプルプロジェクトは環境的なポータビリティを実現しています。
非ポータブル(Non-portable)
非ポータブルなプロジェクトは、特定の状況および条件下(例えば、ローカルマシンなど)でしかビルドできません。プロジェクトを別のマシンに移動する計画が絶対にない場合を除き、非ポータブルにすることは避けてください。非ポータブルということは、定義が1つしかない環境的ポータビリティと同じです。つまり、プロジェクトは1つの環境設定下でしかビルドできません。
プロジェクトがどのレベルに位置するかを判断する方法については、「補足説明4 ポータビリティレベルの判断方法」を参照してください。
ターゲットユーザーだけを対象とするようにビルドを調整する必要があります。プロジェクトのポータビリティの調整に悩む前に、次の質問に答えてください。
- ワイドなポータビリティ
- インハウスのポータビリティ
- 環境的なポータビリティ
- 非ポータブル
Mavenで一般的なポータビリティ問題を解決する
必要なポータビリティのレベルを定義したら、次の問題はプロジェクトをそのレベルまでまたはそのレベル以上に上げることです。皆さんのプロジェクトが自己完結型で、ユーザーが修正を加えたり標準Maven以外のものをインストールしたりせずにそのプロジェクトをビルドして実行できるのであれば、もう何も言うことはありません。皆さんのプロジェクトは既にワイドなポータビリティを実現しているので、ここで読むのを止めてかまいません。しかし、重要なプロジェクトである場合は、このまま続けてお読みださい。
systemスコープを回避する
dependency要素では、次のようなprovidedスコープとsystemスコープを回避します。
<dependency> <groupId>com.closedsource</groupId> <artifactId>sometool</artifactId> <version>1</version> <scope>system</scope> <systemPath>/usr/lib/sometool.jar</systemPath> </dependency>
systemスコープを回避できない場合は、systemPath
のプロパティを使います。これで、ビルド環境ごとにそのプロパティを設定/変更することができます。ただし、一番良い手段は、dependencyをインハウスのMavenリポジトリ(以下参照)にデプロイし、そのdependencyを標準スコープとして使う方法です(前述のステートメントについての詳細は、「補足説明5 絶対値をグループ化する」を参照してください)。
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jboss-maven-plugin</artifactId> <configuration> <jbossHome>${jboss.home}</jbossHome> </configuration> </plugin> </plugins> </build> ... <properties> <jboss.home>${env.JBOSS_HOME}</jboss.home> </properties> </project>
<profiles> <profile> <id>my-jboss</id> <properties> <jboss.home>${user.home}/jboss</jboss.home> </properties> </profile> </profiles>
フィルタを活用する
フィルタは積極的に使用しましょう。Mavenではフィルタを使用することで、プロパティの置き換え(ビルドライフサイクルでは*-processフェーズでマークされます)を外部化し、移動しやすく構成可能なpropertiesファイルにすることができます。次のように、ビルド内でフィルタ処理する必要がある標準Javaのpropertiesファイルのリストを追加するだけです。
<filters> <filter>datasource.properties</filter> </filters>
本稿のサンプルプロジェクトでは、「datasource.properties」ファイルは基本ビルドディレクトリ(${buildDir}
)内にあり、「jdbc.url」に関する「名前=値」のペアを含んでいます。
jdbc.url=jdbc:driver://localhost/myDB
リソースをフィルタ処理する場合、プロジェクトは一致するすべてのプロパティ名を対応する値に置き換えて、フィルタリストを取り入れます。フィルタ処理対象のリソースの定義にはresourcesビルド要素を使います。例えば、次のブロックでは、プロジェクトにフィルタ処理する必要があるXMLリソースがあることと、結果を「META-INF」ディレクトリに挿入することが指定されています。
<resources> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> <targetPath>META-INF</targetPath> <includes> <include>*.xml</include> </includes> </resource> </resources>
サンプルプロジェクトの例を実行するには、コマンドラインでprocess-resourcesフェーズを実行します。これにより、次の「datasource.xml」リソースファイルが、
<datasource> <jdbc-url>${jdbc.url}</jdbc-url> </datasource>
次のように変換されます(「target/META-INF」ディレクトリ内)。
<datasource> <jdbc-url>jdbc:driver://localhost/myDB</jdbc-url> </datasource>