JAR ファイルの仕様

目次

はじめに

JAR ファイルは普及している ZIP ファイル形式に基づくファイル形式で、多数のファイルを 1 つにまとめるために使用されます。 JAR ファイルは、基本的にはオプションの META-INF ディレクトリを格納する ZIP ファイルです。JAR ファイルは、コマンド行 jar ツールを使うか、Java プラットフォームで java.util.jar API を使って作成できます。JAR ファイルの名前には制約がないため、各プラットフォームで許可されているファイル名を使うことができます。

多くの場合、JAR ファイルは、単純な Java のクラスファイルまたはリソースのアーカイブではありません。JAR ファイルは、アプリケーションおよび拡張機能の構築ブロックとして使います。META-INF ディレクトリが存在する場合は、セキュリティ、バージョン管理、拡張機能、サービスなど、パッケージおよび拡張機能の構成データを格納するときに使います。

META-INF ディレクトリ

Java 2 プラットフォームでは、アプリケーション、拡張機能、クラスローダ、およびサービスが構成されるときに、META-INF ディレクトリから次のファイルとディレクトリの認識および解釈が行われます。 拡張機能およびパッケージ関連のデータの定義に使うマニフェストファイル このファイルは、jar ツールの新しいオプション「-i」によって生成されます。アプリケーションまたは拡張機能に定義されているパッケージの位置情報が格納されます。JarIndex 実装に組み込まれており、クラスのロード処理を速くするためにクラスローダによって使われます。 JAR ファイルの署名ファイルです。 「x」は、ベースファイル名を表します。 同じベースファイル名を持つ署名ファイルに関連付けられた署名ブロックファイル。対応する署名ファイルのデジタル署名が格納されます。 サービスプロバイダ構成ファイルがすべて格納されます。

名前-値ペアおよびセクション

各構成ファイルの内容を詳細に設定する前に、形式の規則をいくつか定義する必要があります。ほとんどのケースで、マニフェストファイルまたは署名ファイルに含まれる情報は、RFC822 で規定されるいわゆる「名前:値」ペアとして表されます。 「名前:値」ペアは、ヘッダまたは属性とも呼ばれます。

名前-値ペアのグループを「セクション」と呼びます。セクションはほかのセクションと空白行で分けられます。

バイナリデータは、どの形式であれ base64 で表されます。行の長さが 72 バイ トを超えるようなバイナリデータについては継続が必要です。バイナリデータの例はダイジェストおよび署名です。

実装によっては、65535 バイトまでのヘッダ値がサポートされます。

このドキュメントの仕様には同一の文法が使われており、終端記号は固定幅のフォントで示され、終端以外の記号はイタリック書体で示されています。

仕様:

section: *header +newline
nonempty-section: +header +newline
  newline:                      CR LF | LF | CR (not followed by LF)
header: name :value
name: alphanum *headerchar
value: SPACE *otherchar newline *continuation
continuation: SPACE *otherchar newline
alphanum: {A-Z} | {a-z} | {0-9}
headerchar: alphanum | - | _
otherchar: any UTF-8 character except NUL, CR and LF

; Also:To prevent mangling of files sent via straight e-mail, no
; header will start with the four letters "From".

上の仕様で定義されている終端以外の記号は、以降の仕様で使われています。

JAR マニフェスト

概要

JAR ファイルマニフェストは、メインセクションと各 JAR ファイルエントリの複数の個別セクションで構成され、それぞれ改行文字で区切られています。メインセクションおよび個別セクションは、すでに説明したセクションの構文に準拠しています。各セクションには、固有の制約と規則があります。

メインセクションには、JAR ファイル自体のセキュリティと構成情報以外に、その JAR ファイルが使われているアプリケーションまたは拡張機能のセキュリティと構成情報を指定します。また、各マニフェストエントリに適用されるメイン属性も定義します。 メインセクションの属性に、「Name」と同じ名前を付けることはできません。メインセクションは、空白行で終わります。

個別セクションには、この JAR ファイルに格納されているパッケージまたはファイルのさまざまな属性を定義します。アーカイブ内のすべてのファイルをマニフェストのエントリにリストする必要はありませんが、署名するすべてのファイルはリストする必要があります。マニフェストファイル自体はリストしないでください。各セクションは、「Name」という名前の属性で始まる必要があります。この属性の値は、ファイルを起点とした相対パス、またはアーカイブの外部のデータを参照する絶対 URL でなければなりません。

1 つのファイルエントリに複数の個別セクションがある場合は、これらのセクションの属性はマージされます。特定の属性の値がセクションによって異なる場合は、最後のセクションの値が認識されます。

理解できない属性は無視されます。アプリケーションによって使われる実装固有の情報に、理解できない属性が含まれていることがあります。

マニフェストの仕様

manifest-file:                    main-section newline *individual-section
  main-section:                    version-info newline *main-attribute
  version-info:                      Manifest-Version :version-number
  version-number :               digit+{.digit+}*
  main-attribute:                 (any legitimate main attribute) newline
  individual-section:             Name :value newline *perentry-attribute
  perentry-attribute:            (any legitimate perentry attribute) newline
  newline :                            CR LF | LF | CR (not followed by LF)
   digit:                                {0-9} 

上の仕様では、メインセクションに指定した属性はメイン属性、個別セクションに指定した属性はエントリ別属性として参照されます。属性によっては、メインセクションと個別セクションの両方で指定できます。 この場合、そのエントリでは、メイン属性の値はエントリ別属性の値によってオーバーライドされます。 2 つのタイプの属性は、以下のように定義されます。

メイン属性

メイン属性は、マニフェストのメインセクションに指定されている属性です。メイン属性は、次のグループに分類されます。

エントリ別属性

エントリ別属性は、そのマニフェストエントリが関連付けられている個別の JAR ファイルエントリにだけ適用されます。 メインセクションに同じ属性がある場合は、メイン属性の値はエントリ別属性の値によってオーバーライドされます。たとえば、JAR ファイル a.jar には、次のコンテンツがマニフェストに定義されています。 この場合、foo.bar パッケージを除き、a.jar 内に保管されているパッケージがすべてシールされます。

エントリ別属性は、次のグループに分類されます。

署名付き JAR ファイル

概要

JAR ファイルに署名するには、コマンド行 jarsigner ツールを使うか、java.security API を直接使います。jarsigner ツールを使って JAR ファイルに署名した場合は、ファイルエントリごとに署名されます。java.security API を使った場合は、JAR ファイルのサブセットに署名されます。 署名された JAR ファイルは、ファイルのマニフェストが更新され、META-INF ディレクトリに署名ファイルと署名ブロックファイルが追加されていますが、それ以外の内容は元の JAR ファイルと同じです。 jarsigner を使わない場合は、署名プログラムは署名ファイルと署名ブロックファイルの両方を構築する必要があります。

署名付き JAR ファイルのファイルエントリが署名されると、そのエントリが以前にマニフェスト内に存在していなかった場合は、エントリ単位にマニフェストエントリが作成されます。各マニフェストエントリには、1 つまたは複数のダイジェスト属性とオプションの Magic 属性をリストします。

署名ファイル

各署名者は、拡張子が .SF の署名ファイルによって表されます。このファイルの大部分は、マニフェストファイルと同じです。このファイルは、メインセクションといくつかの個別エントリで構成されます。メインセクションの情報は署名者が定義しますが、特定の jar ファイルエントリ固有の情報ではありません。個別エントリの名前は、マニフェストファイル内にも定義する必要があります。各個別エントリには、少なくともマニフェストファイルの対応するエントリのダイジェストが含まれている必要があります。

マニフェストファイルに表示されるが署名ファイルには表示されないパスまたは URL は、計算に使用されません。

署名の検証

マニフェストが最初に分析されるとき、署名が最初に検証されます。この検証結果を効率化のために記録できます。これは実際のアーカイブファイルでなく、署名自体を検証するだけです。

ファイルを検証するためには、署名ファイルのダイジェスト値をマニフェストファイルの対応するエントリから計算したダイジェスト値と比較します。 次に、マニフェストファイルのダイジェストの値を、「Name:」属性から参照される実際のデータ (相対ファイルパスまたは URL) から計算されたダイジェストと比較します。

マニフェストファイルの例:

対応する署名ファイルは次のようになります。

Magic 属性

特定のマニフェストエントリ上で署名を有効化するためにもう 1 つ必要なのは、そのエントリのマニフェストエントリ内の Magic キーペア値の値をベリファイアが理解することです。

Magic 属性はオプションですが、パーサがそのエントリの署名を検証する場合は、エントリの Magic キーの値を理解する必要があります。

Magic 属性の値は、コンマで区切られたコンテキスト固有の文字列のセットです。コンマの前後の空白は無視されます。大文字小文字も無視されます。Magic 属性の正確な意味はアプリケーションによって異なります。これらの属性は、マニフェストエントリに含まれるハッシュ値の計算方法を示し、そのため署名の正しい検証には欠くことのできないものです。このキーワードは、動的または埋め込みコンテンツ、多国語ドキュメント用の複数ハッシュなどに使用します。

以下に、マニフェストファイルでの Magic 属性の使用例を 2 つ示します。

Name:http://www.scripts.com/index#script1
        SHA-Digest:(base64 representation of SHA hash)
        Magic:JavaScript, Dynamic

Name:http://www.tourist.com/guide.html
        SHA-Digest:(base64 representation of SHA hash)
        SHA-Digest-French:(base64 representation of SHA hash)
        SHA-Digest-German:(base64 representation of SHA hash)
        Magic:Multilingual

最初の例では、これら Magic の値は http 問い合わせの結果がドキュメント自身ではなく、ドキュメントに埋め込まれたスクリプトであり、またそのスクリプトが動的に生成されるということを示します。この 2 つの情報は、マニフェストのダイジェスト値と比較し、有効な署名と比較するハッシュ値の計算方法を示します。

第 2 の例では、Magic 値は検索されたドキュメントの内容は特定の言語であるという合意を示し、検証のためのダイジェストの値は検索されたドキュメントを記述する言語に依存することを示します。

デジタル署名

デジタル署名とは、署名された .SF 署名ファイルです。これらはバイナリファイルであり、人間が解釈することは意図されていません。

デジタル署名ファイルは .SF ファイルと同じファイル名を持ちますが、異なる拡張子を持っています。拡張子はデジタル署名の型によって変化します。

.RSA      (PKCS7 signature, MD5 + RSA)
  .DSA      (PKCS7 signature, DSA)
  .PGP      (Pretty Good Privacy Signature)

外部署名データをサポートしないこれらの形式については、ファイルは .SF ファイルの署名済みコピーからなります。あるデータは重複する可能性があるため、ベリファイアは 2 つのファイルを比較する必要があります。

外部データをサポートする形式は、.SF ファイルを参照するか、暗黙的な参照によって計算を実行します。

.SF ファイルは複数のデジタル署名を持つ可能性がありますが、これらの署名は同じ正当なエンティティによって生成される必要があります。

ファイル名の拡張子には、1 〜 3 文字の英数字を使うことができます。認識されない拡張子は無視されます。

マニフェストおよび署名ファイルについて

ここでは、マニフェストおよび署名ファイルに適用されるその他の制約および規則について説明します。

JAR インデックス

概要

1.3 から、ネットワークアプリケーション、特にアプレットのクラスローダによるクラス検索処理を最適化するために、JarIndex が導入されています。アプレットのクラスローダの基本機能では、単純な線形検索アルゴリズムによって、内部検索パスの各要素が検索されます。内部検索パスは、「ARCHIVE」タグまたは「Class-Path」メイン属性から構築されます。クラスまたはリソースが検出されるまで、クラスローダによって検索パスの各要素がダウンロードされて開かれます。クラスローダによって、存在しないリソースの検索が行われた場合、アプリケーションまたはアプレットの jar ファイルがすべてダウンロードされることになります。この結果、大きなネットワークアプリケーションおよびアプレットの場合は、起動および応答が遅くなり、ネットワーク帯域幅が浪費される可能性があります。JarIndex 機構では、アプレットに定義されている jar ファイルのコンテンツがすべて収集され、アプレットのクラスパスにある最初の jar ファイルのインデックスファイルにこの情報が格納されます。最初の jar ファイルがダウンロードされると、アプレットクラスローダでは、収集されたコンテンツ情報を使って効率的に jar ファイルがダウンロードされます。

既存の jar ツールの機能も拡張されています。jar ファイルのリストが検査されてから、クラスおよびリソースが属している jar ファイルについてのディレクトリ情報が生成されます。このディレクトリ情報は、ルート jar ファイルの META-INF ディレクトリの INDEX.LIST という名前の、単純なテキストファイル内に格納されます。クラスローダによって、ルート jar ファイルがロードされ、INDEX.LIST ファイルが読み込まれます。次に、そのファイルを使って、ファイルとパッケージ名から jar ファイル名のリストへのマッピングを格納したハッシュテーブルが構築されます。クラスローダによってクラスまたはリソースが検索される場合、ハッシュテーブルを問い合わせて、適切な jar ファイルが検出されてから、必要に応じてダウンロードされます。

クラスローダによって、特定の jar ファイルで INDEX.LIST ファイルが検出されると、そのファイルにリストされた情報は常に信頼されます。クラスローダによって特定のクラスのマッピングが検出されたあとで、リンクをたどってもそのクラスが検出されなかった場合は、InvalidJarIndexException がスローされます。この例外が発生した場合は、アプリケーション開発者は、拡張機能に対して jar ツールを実行し直し、インデックスファイルに正しい情報を取得する必要があります。

大量の領域オーバーヘッドの発生をアプリケーションで回避し、メモリ内のハッシュテーブルを高速で構築するために、INDEX.LIST ファイルの容量はできる限り小さくなるように管理されます。クラスのパッケージ名が null でない場合は、マッピングはパッケージレベルで記録されます。通常は、1 つのパッケージが 1 つの jar ファイルにマッピングされます。 ある特定のパッケージが 1 つ以上の jar ファイルにわたる場合は、このパッケージは jar ファイルのリストにマップされます。リソースファイルにディレクトリの接頭辞がある場合は、マッピングはディレクトリレベルで記録されます。 パッケージ名が null のクラスの場合およびルートディレクトリにリソースファイルが格納されている場合にだけ、マッピングがファイルレベルで記録されます。

インデックスファイルの仕様

INDEX.LIST ファイルには、1 つ以上のセクションが含まれ、それぞれ 1 行の空白行で区切られています。 セクションごとに 1 つの jar ファイルのコンテンツが定義されています。各セクションでは、jar ファイルのパス名を定義するヘッダのあとに、パッケージまたはファイル名が各行に定義されたリストが続きます。 すべての jar ファイルのパスは、ルート jar ファイルのコードベースを起点とする相対パスです。これらのパス名は、バンドル型拡張機能が現在の拡張機能機構によって解釈される方法と同じ方法で解決されます。

インデックスファイルのファイルまたはパッケージ名に、ASCII 以外の文字が使われているときは、UTF-8 エンコーディングが使われます。

仕様

index file :                   version-info blankline section*
    version-info :              JarIndex-Version:version-number
    version-number :        digit+{.digit+}*
    section :                      body blankline
    body :                         header name*
    header :                      char+.jar newline
    name :                        char+ newline
    char :                          any valid Unicode character except NULL, CR and LF
    blankline:                    newline newline
    newline :                      CR LF | LF | CR (not followed by LF)
    digit:                           {0-9}
 
INDEX.LIST ファイルは、jar -i. を実行することによって生成されます。詳細については jar のマニュアルページを参照してください。

下位互換性

新しいクラスのロード方式は、現在の拡張機能機構を基にして開発されたアプリケーションと完全な下位互換性があります。 クラスローダによって最初の jar ファイルがロードされ、META-INF ディレクトリ内で INDEX.LIST ファイルが検出されたときは、インデックスハッシュテーブルが構築され、その拡張機能に対して新しいロード方式が使われます。INDEX.LIST ファイルが検出されなかったときは、クラスローダでは元の線形検索アルゴリズムが使われます。

サービスプロバイダ

概要

META-INF/services ディレクトリ内のファイルは、サービスプロバイダの構成ファイルです。サービスとは、よく使われるインタフェースとクラス (通常は abstract) のセットのことです。サービスプロバイダとは、サービスの特定の実装のことです。通常、プロバイダのクラスによって、サービス自体に定義されているクラスのインタフェースとサブクラスが実装されます。サービスプロバイダを Java プラットフォームの実装にインストールするときは、拡張機能の形式、つまり、拡張機能の通常のディレクトリに配置される jar ファイルの形式で行われます。プロバイダを利用可能にするには、アプレットまたはアプリケーションのクラスパスに追加するか、プラットフォーム固有の方法を使います。

サービスは、abstract クラスによって表現されます。特定のサービスのプロバイダは、サービスのクラスを継承したいくつかの具象クラスで構成されています。サービスのクラスには、プロバイダ固有のデータおよびコードが含まれます。通常、プロバイダクラスには、プロバイダ自体がすべて含まれることはありません。要求時に実際のプロバイダを作成できるコードと、プロバイダが特定の要求を満たすことができるかどうかを識別するために必要な情報で構成されるプロキシになっています。プロバイダクラスの内容は、個別のサービスに大きく依存します。1 つのクラスまたはインタフェースでプロバイダクラスを統合することはできません。このため、このようなクラスは定義されていません。 プロバイダクラスには、ルックアップ中にインスタンスを生成できるように、引数を取らないコンストラクタが必要です。

プロバイダ構成ファイル

サービスプロバイダは、リソースディレクトリ META-INF/services にプロバイダ構成ファイルを配置することによって識別されます。このファイルの名前は、完全指定された abstract サービスクラス名で構成する必要があります。このファイルには、改行文字で区切られた、一意の具象プロバイダクラス名のリストを含める必要があります。空白、タブ文字、および空白行は無視されます。コメント文字は「#」(0x23) です。各行では、最初のコメント文字以降の文字はすべて無視されます。このファイルは、UTF-8 方式で符号化する必要があります。

java.io.spi.CharCodec という名前のサービスクラスを想定します。このクラスには、次の 2 つの abstract メソッドがあります。

public abstract CharEncoder getEncoder(String encodingName);
public abstract CharDecoder getDecoder(String encodingName);

これらのメソッドは、渡されたエンコーディングを変換できない場合、適切なオブジェクトまたは null を返します。標準の CharCodec プロバイダでは、複数のエンコーディングがサポートされています。

sun.io.StandardCodec が CharCodec サービスのプロバイダの場合は、jar ファイルに META-INF/services/java.io.spi.CharCodec ファイルが含まれます。このファイルには、次の行が含まれます。

sun.io.StandardCodec # Standard codecs for the platform

特定のエンコーディング名のエンコーダを検索するには、内部の I/O コードによって次のような処理が行われます。

CharEncoder getEncoder(String encodingName) {
       Iterator ps = Service.providers(CharCodec.class);
       while (ps.hasNext()) {
           CharCodec cc = (CharCodec)ps.next();
           CharEncoder ce = cc.getEncoder(encodingName);
           if (ce != null)
               return ce;
       }
       return null;
   }
 

プロバイダのルックアップ機構は、常に呼び出し側のセキュリティコンテキストで実行されます。信頼できるシステムコードでは、通常、このクラスのメソッドは特権付きのセキュリティコンテキストから呼び出す必要があります。

API の詳細

パッケージ java.util.jar

関連項目

パッケージ java.security
パッケージ java.util.zip

Copyright © 1999 Sun Microsystems, Inc.All Rights Reserved.

Sun
Java ソフトウェア