じゃぁ、1つで済ませましょ (2) [アプリケーションとして動作するように設定する: Manifestとクラスパス]
2002-03-10
JAR(Java Archive)についての第2回目です。前回、Java2 SDKに付属するjarツールの使い方について見ていきました。その際に、Windowsではjarファイルがjavaコマンドと対応づけられており、jarファイルをダブルクリックして、アプリケーションを起動できる、ということをお伝えしました。しかし、ただ単に、classファイルをjarに固めれば、ダブルクリックで起動できるわけではありません。
- jarツール
- jarファイルとManifest、クラスパス
- プログラムからjarファイルを使う
- 入れ子のJARの取り扱い
今回は、jarファイルをアプリケーションとして扱えるようにするために、する必要のある設定について述べていきましょう。主なトピックとして、Manifestファイルやクラスパスを挙げておきます。
■起こして動かす
javaのアプリケーションの起動のしかたを振り返っておきましょう。例えば、アプリケーションを起動する場合には、以下のように行います。
| D:\Develop\JarView\class> java -classpath
jakarta-regexp-1.2.jar jvMain |
| |
javaアプリケーションの起動 |
javaではアプリケーションを起動する場合、javaコマンドにmainメソッドを含んだクラスを指定します。標準で提供されていないクラスを使用する場合には、環境変数のCLASSPATHや、javaコマンドに-classpathオプションで、そのクラスが含まれるパスもしくはjarファイルを指定します。
jarファイルを用いてアプリケーションを構成する際にも、同様に以下を何らかの形で指定する必要があります。
このようなアプリケーション固有の情報は、Manifestファイルに記述します。
■メタなはなし
jarファイルの中身をWinZipなどで見てみると、ファイルが並んでいることがわかります。この1つ1つをjarエントリと呼び、Manifestの属性には、このjarエントリそれぞれを対象とするものと、jarファイル全体に対する設定を行うものがあります。
jarツールを使用すると、最低限のManifestファイルを自動で作成します。meta-infというフォルダが作成され、そこにManifest.mfという名称で置かれます。meta-infフォルダには、Manifestファイル以外にも、jarファイルに関する情報を記述した複数のファイルが置かれます。
Java2 SDKのバージョン1.3.1_02に付属するjarツールが自動で生成するManifest.mfファイルの内容は、このようになっています。それ以外の属性については、独自に定義して追加することになります。独自の属性は、テキストファイルで作成します。ファイル名は、どのような名称でもかまいません。jarコマンドで、独自に作成したManifestを指定し、Manifestの更新を実行すれば、meta-inf\Manifest.mfにエントリが追加されます。
| D:\Develop\MorePaste\1.1\app> jar
umf Manifest2.txt
mpapp.jar |
| Manifestの更新 |
jarファイルの更新 |
よく使用することになると思われるManifestの属性には、以下のようなものがあります。そのほかに、アプレットをjarに固めた場合に使用する属性など、いろいろありますが、以下を知っていればこと足りるでしょう。
| jarファイル全体に対する属性 |
| Manifest-Version |
Manifestの属性に関する仕様のバージョン |
| Created-By |
Manifestファイルを生成したjavaの実装に関して、そのバージョンとそれを作成したベンダー
|
| Class-Path |
jarファイルに格納されたアプリケーションやライブラリが必要とする、ライブラリへのパス。相対URLで記述し、複数のパスを書く場合は、区切り文字として空白文字を使用する |
| Main-Class |
stand aloneのアプリケーションを定義するための属性。起動時にmainメソッドを呼び出す、クラス名を記述する |
| jarエントリごとの属性 |
| Name |
jarのエントリごとの属性を定義する際に、どのエントリに対するものかを示すために、それぞれの先頭でエントリ名を記述する |
| Java-Bean |
true/falseで記述し、そのエントリがJavaBeanであるかどうか示す |
"Manifest-Version"や"Created-By"は、jarコマンドが自動的に作成します。また、"Java-Bean"は、IDE(統合開発環境)を使用していれば、多くの場合、jarにパッキングしてBeanを作成する処理の中で自動的に設定してくれるでしょう。swingのコンポーネントは、全て、JavaBeanとして定義されており、Manifestもこのようになっています。
mpapp.jarがアプリケーションのクラスを格納したjarで、mplib.jarを使用する場合には、Class-Pathにmplib.jarを指定します。また、mpapp.jarを起動する際に呼び出すmainメソッドがMPMainクラスに実装されている場合には、Main-ClassにMPMainを指定します。
このために作成するManifestファイルは、以下ようになります。jarファイルを更新すると、Manifest.mfはこのような内容になります。
Main-Class: MPMain
Class-Path: mplib.jar
|
| Manifest2.txt(ファイル名は何でもよい) |
独自に作成したManifestエントリ |
■目録はこちら
前回、Java2のバージョン1.3からJarIndexという機能がサポートされている、ということに触れました。jarツールで、JarIndexを作成すると、jarファイルがどのように変更されるか、ということについて見ておきましょう。
JarIndexを作成すると、meta-infフォルダに、Index.listというファイルが追加されます。このファイルには、アプリケーションやアプレットを構成するパッケージがjarファイルごとに列挙されています。javaのローダーは、起動する際にJarIndexがあると、パッケージが含まれているjarファイルのみを開きます。このため、効率的にクラスをロードすることができるようになります。
なお、jarIndexを作成する際には、ManifestファイルのClass-Pathエントリが参照されます。mpapp.jarでClass-Pathとしてmplib.jarを定義した場合としない場合では、前者の場合にはこのようなIndex.listになりますが、後者の場合にはこのようになります。
■探せ、探せ
jarにパッキングしていても、いなくても、何らかの形で、javaのアプリケーションやアプレットが起動されると、そこで使われるクラスを探す、という行為が行われます。これは、次のような順序になっています。
- ブートストラップクラスの中から探す
Javaのプラットフォームを構成するクラスで、rt.jarやi18n.jarに含まれるクラス
これらのクラスは、jre/libディレクトリに格納されている
- 拡張クラスの中から探す
Java Extention Mechanismを使用しているクラスで、拡張ディレクトリjre/lib/extに格納されたjarに含まれるクラス
- ユーザクラスの中から探す
CLASSPATH環境変数や、-classpathオプションで指定されたクラス
Java2より以前のバージョンでは、全てのクラスに関して、CLASSPATH環境変数や-classpathオプションで明示的に示す必要がありました。しかしながらJava2になり、Extension
Mechanismという仕組みが作られて、ブートストラップクラスと拡張クラスは自動的にクラスを探すリストに追加されるようになりました。
Extension Mechanismの仕組みは、整理すると以下のような要素で構成されます。
- パッケージをjarファイルで提供し、複数のjarファイル間で依存関係を定義する仕組みを用意する。このようにすることで、アプリケーションを複数のモジュールで構成することができるようにする
- クラスをロードする仕組みを拡張して、組み込みのパッケージからクラスを探して、見つからない場合にはアプリケーション独自のパッケージから探すようにする
jarファイルのManifestで、独自にClass-Pathエントリを記述すれば、このような仕組みの中で使用されることになります。また、JarIndexは、このような仕組みを効率化するために、用意された手段であることがわかります。
■拡張ディレクトリとクラスパスであれこれ
ところで、SDKのドキュメントでは、jarファイルを使用する場合にはManifestのClass-Pathエントリに記述する、ということが強調されている印象を受けます。これと、CLASSPATH環境変数や-classpathオプションを一緒に使うことはできないのでしょうか。試してみると、javaコマンドの引数にjarファイルを指定したり、jarファイルをダブルクリックして起動する場合には、環境変数やオプションは無視されました。明確には謳われていないようですが、これらは排他的に動作するというのがルールのようです。
(訂正: 他を上書きすると明確に記述されていました。)
また、拡張ディレクトリの意味合いですが、Java2の環境をインストールしただけでは、そのフォルダには何も置かれません。以前は、拡張クラスは、javaxで始まるパッケージのクラスを指したようでしたが、現在は標準に取り込まれてしまっています。経緯はいろいろあったようですが、とにかく位置づけが曖昧なったことは事実でです。例に立ち返ると、mplib.jarをjar/lib/extに置ければ、Class-Pathに記述しなくても、ちゃんと呼び出されて動作します。しかしながら、このような独自に構築したアプリケーション間でしか共有されないライブラリを、拡張ディレクトリに置くのはアプリケーションの配布を考えた場合、現実的ではないと言えます。
一方で、Java2 Enterprise Editionの実装などでは、そこに付属の起動スクリプト内でCLASSPATHにjavaxで始まるクラスを追加する、ということを行うものが多々あります。このため、以前に自分でダウンロードしてきた古いバージョンのパッケージがjre/lib/extに置いておいてあったりすると、起動スクリプト内で一時的に更新されたCLASSPATHとバージョンが衝突することがあります。以前に経験したのは、JAXPなどXML関係のAPIで、これらはいろいろなところにバンドルされて、頻繁に使われる一方、更新も多いライブラリです。JavaHelpなども、同じ状況になる可能性を持つ性格のAPIだと言えるでしょう。
なお、拡張ディレクトリの位置は、どのjavaコマンドが呼び出されているかに依存します。通常、コマンドプロンプトでjavaコマンドを使用する場合には、環境変数のPATHで指定されたものが起動されます。jarファイルをダブルクリックした場合には、デフォルトのインストール状態で調べてみると、Windows環境ではC:\Program
Files\JavaSoftの下にインストールされたjavawコマンドがjarファイルに関連づけられています。ダブルクリックして起動する場合と、コマンドラインでコンパイル、デバッグしている時点では、違うjavaコマンドが呼ばれている可能性がありますから、どちらでも同じように動作させるためには、両方のjre/lib/extにライブラリのjarを入れておく必要があります。
jarファイルから始まり、Manifest、Extension Mechanism、クラスパスへと話題を移しながら見てきました。クラスパスの設定のしかたや、拡張ディレクトリの使い方については、明確な指針が無く、仕様だけが残って形骸化した、というのが現状だと言えます。このように、変に歴史を重ねてしまうと、上辺だけではなかなか理解が難しくなってしまいます。わかりにくいのは当然でしょう。
最後に、SDKに付属のTool
Documentationを見ていて、extcheckなるものが付属していることに気がつきました。これを使うと、バージョンの衝突のチェックができるようです。自分でも暇を見て、使ってみたいと思います。
次回は、プログラムからjarを使う、ということについて紹介する予定です。

|