目次 | 前の項目 | 次の項目 JavaTM 製品のバージョン管理の仕様

Java™ 製品のバージョン管理


第 1 章

1.1 はじめに

どのようなシステムでも、時とともに更新していくシステムにはサポートを提供する必要があります。通常、既存のシステムには、変更の適用方法を指定する規約および機構が定められています。これらのシステムは、ソフトウェアプログラムがコンピュータにインストールされていることを前提としています。一般に、開発者は、必要とするほかのパッケージのバージョンを指定できており、インストールプロセスは、システムの検証と設定に役立っています。

オープンな分散システムでは、既存システムが静的であるという仮定は当てはまらず、パッケージが変化する方法と時期を制御することは不可能なため、また的確な運用はパッケージ間の多くの依存関係に基づいているため、その更新はさらに困難です。オープンで信頼性が高く、かつ拡張性のある分散システムの構築という目標を達成するには、システムパッケージの更新方法を規定する最新の規約と機構のセットが必要となります。

このドキュメントでは、以下について指定します。

1.2 要件

分散システム内の変更は、エンドユーザ、サポート組織、Web 管理者、および開発者に重大な影響を及ぼします。

これらのグループごとに、更新を続けるネットワーク製品に対する要件を満たさねばなりません。

ユーザ

ユーザには、時の経過とともに Java ベース製品は信頼性および互換性が向上するという確信を持たせる必要があります。ユーザがアップグレードを躊躇する場合、「Write Once, Run Anywhere」という哲学に対するユーザの確信を築くことが必要です。Java では、「アップグレードしたらどこかに障害が生じる」とか「ほかのユーザが使用するデータを読み書きすることができなくなってしまう」という心配がないようにしなければなりません。

製品サポート

製品サポート組織にとって、使用されている製品とその使用環境、および製品パッケージの完成度を容易にかつ正確に識別できることは非常に重要です。

Web マスターおよび管理者

Web マスター、管理者、およびサービスプロバイダは、信頼性が高く、サポート可能な方法で、Web またはネットワークファイルシステムを介してクライアントにアプリケーションを配備する必要があります。

製品開発者

製品開発者は、ユーザ、管理者、およびサポートスタッフの要求を満たすアプリケーションおよびライブラリの記述および配置方法を理解する必要があります。開発者には、以下の製品およびパッケージの開発が求められます。

1.3 分散システムの更新で生じる問題

オープンな分散システムでは、パッケージが拡大し、個別に更新される場合に、多くの問題が発生することがあります。public インタフェースを使用する際に、インタフェース固有の特定動作が維持されないと、予期しない方法でシステムに障害が発生する場合があります。オープンシステムは、異なる会社や組織が作成したパッケージで構成されています。これらの組織は、相互に連絡を取りながら運営されているわけではなく、独自のスケジュールで新製品を発表したり、アップグレードしたりします。アップグレードされたこれらの製品の配布には時間がかかり、しかもアップグレードを採用するかどうかという問題もあります。

Java では、ローカルおよび分散システムのコンポーネントは、public インタフェースおよびほかのパッケージの動作規定に依存しています。それぞれのパッケージは時とともに更新されます。パッケージが正しく動作するためには、そのパッケージが依存するパッケージが、アップグレード後も期待される動作を継続して提供できなければなりません。

分散システムでは、システム全体の状態を把握することはできないため、部分的に整合性を保つことしかできません。システムの各プロセスおよび各パッケージは、システムの現在の状態を部分的にしか見ておらず、分散システムのほかの部分に情報を要求することにより情報を蓄積していきます。情報の断片は、それが起動したアプレット、ロードされたクラス、呼び出されたリモートメソッド、または取得された Web ページのどれからであっても、すでに得ているシステムの情報と一貫性を保って使用できるように、注意深く扱う必要があります。

ロードされたクラス内の不一致により、クラスの検証ができない、行われた不正なクラス計算を認識できない、ユーザが要求した機能が特定不能な障害を示すなどの様々なエラーが発生する可能性があります。

典型的な問題が生じるのは、以下のような場合です。

これらの問題は、動的にロードされたパッケージ間の非整合性から発生するため、予防したり、直接解決することはできません。これらのパッケージは 1 人のシステム管理者の管理下にあるわけではないため、現在の設定管理技術では特定することができません。

1.4 更新を考慮した設計

これらの問題に対処し、前述の要件を満たす鍵となるのは、パッケージおよびシステムのパッケージ化を注意深く設計し、一貫した単位ごとの更新、配布、およびロードを可能にすることです。大量生産される製品で一般的なのは、現場での交換が可能な単位という概念です。これは製品の最小単位で、仕様およびサプライヤによって識別されます。また配布、再配布、および障害がある場合には交換も可能です。同様のモデルは、ソフトウェアの配布に利用されています。製品には名前やバージョン番号があり、1 つまたは複数の仕様に準拠し、ネットワークまたは CD-ROM によって配布されます。また問題点もサポート組織に報告することができます。これらのパッケージは、配布、使用、検査、置換、また必要な場合にはアップグレードが可能な最小単位です。パッケージはほかのパッケージと組み合わせることができますが、そのあともパッケージごとに識別、検証、および配布が可能です。

Java 言語ベースのパッケージ機構は、置き換え可能な単位という考え方とよく適合します。Java パッケージは、public インタフェースだけを公開し、ほかのパッケージの public インタフェースだけを使用します。Java 言語仕様は、互換性を維持しつつ更新されるパッケージへのアプローチを定義しています。

1.4.1 下位互換に関する Java 言語仕様

Java 言語仕様は、時間の経過にしたがって適正な更新が期待されるパッケージ開発の基礎となります。以前にコンパイルおよび配布したクラスと下位互換を保ちながら、クラスがどのように変化できるのかを定義するからです。健全な更新にとって重要なのは、public、protected、および package インタフェースの安定性と、実装更新時の動作です。Java 言語仕様では、「互換性」のある変化を「既存のインタフェースまたは動作を変更しない変化」と定義しています。それで、あるクラスがメソッドを定義し、そのメソッドが特定の動作をする場合、同じ規約がクラスのそのあとのバージョンすべてでサポートされなければなりません。詳細については、「Java 言語仕様」の第 13 章を参照してください。ここでは、互換性のない変更が 1 つ追加されています。public インタフェースへのメソッドの追加は、互換性がありません。

互換性のない変更は許可されませんが、新規または類似の機能はいつでも、新規または既存のインタフェースやクラスに追加できます。

Java パッケージを更新単位として選択することにより、パッケージおよびクラスのプライベートメソッドが変更され、public および protected クラスとメソッドに外部インタフェースおよびセマンティクスを保ちつつ、パッケージの実装に柔軟性を持たせることができます。

1.4.2 下位互換におけるオブジェクトの直列化仕様

コンポーネント間の持続的記憶領域および通信機能が強力であることは、分散システムにとって重要です。クラスを拡張しつつ記憶領域中の以前のデータをクラスが読めるようにするには、持続的記憶領域を維持しつつコンポーネントを更新しなければなりません。分散システムでは、各コンポーネントの更新速度はそれぞれ異なりますが、コンポーネント間の通信の信頼性は維持できなくてはなりません。

オブジェクトを直列化する際、互換性に関する要件に従うことにより、より新しいまたはより古いバージョンのオブジェクトが予測可能でかつ一貫した方法で通信することが可能になります。詳細については、「Java オブジェクト直列化仕様」の第 5 章を参照してください。

1.5 パッケージバージョンの仕様

確認の必要な作成物のカテゴリがいくつかあり、それには仕様、実装、Java 仮想マシン、および Java Runtime Environment が含まれます。

1.5.1 仕様のバージョン管理

オープンシステムは、仕様には複数の実装が含まれるという考えに基づいています。仕様は、組織や企業の援助のもとに更新されます。仕様に互換性のない複数のバージョンが含まれるというのは、望ましいことではありません。仕様または実装の各バージョンは、単一の次期バージョンにだけ更新しなければなりません。仕様に下位互換性を要求する考え方では、仕様を以前の仕様のスーパーセットと見なすことができます。バージョン仕様の配列順序は 1 通りしかないため、順序付けを意味する特定のセマンティクスを備えたバージョン番号により有意味に識別されます。仕様のバージョン番号には、ピリオドで区切られた数字で構成されるデューイ 10 進数表記法が使用されます。

仕様は、次の項目で識別できます。

1.5.2 仮想マシンのバージョン管理

Java 仮想マシンの実装は、仕様と実装の両方から識別する必要があります。これらのプロパティは、java.lang.System.getProperties を使用してすでに利用可能なものに追加する必要があります。

これらのプロパティへは、java.lang.System.getProperty メソッドを使用してアクセスします。その結果、文字列が返されます。

1.5.3 Java Runtime のバージョン ID

Java Runtime の識別に必要な情報は、すでに「Java 言語仕様」の 20.18.7 で指定されたプロパティにより、System.getProperties を使用して部分的に取得されています。

現時点では、これらのプロパティは、JavaTM Runtime の実装および利用可能なコアクラスを識別します。これらのプロパティは、この JDK が実装する Java 言語仕様を識別しません。

この実装が準拠する Java Runtime Environment 仕様のバージョンの識別には、この他に追加のプロパティが必要です。必要なプロパティは次のとおりです。

これらのプロパティには、java.lang.System.getProperty メソッドを使用してアクセスできます。結果として、値が文字列で返されます。

1.5.4 パッケージのバージョン管理

各 JavaTM パッケージは、クラスファイルおよびオプションのリソースファイルで構成されます。パッケージの中身を識別するために必要な情報も、パッケージの中に格納されています。

この仕様は、パッケージが JavaTM Runtime とともに配布される主要パッケージとして開発されたか、標準拡張として開発されたか、アプレットまたはアプリケーションパッケージとして開発されたかに関係なく、すべてのパッケージに適用されます。

仕様のバージョン番号と異なり、実装のバージョン情報は、パッケージに以前のバージョンと下位互換性があることを示すものではありません。パッケージのバージョン番号は、バグなどの仕様と実装の相違を識別するためのものです。実装の新バージョンは、望ましくない動作または不正確な動作を取り除くために作成されるもので、下位互換性を意図したものではありません。パッケージのバージョンを示す文字列は、任意の固有値を持ち得るため、等価かどうかの比較だけが可能です。詳細については、「1.5.10 実装のバージョン番号を識別用に限定する理由番号」を参照してください。

次の属性名は、パッケージごとに定義されます。それぞれの属性の値は、文字列です。

これらの属性は、マニフェストに保存され、次で説明する java.lang.Package API を使用したプログラムにより取得できます。

1.5.5 パッケージバージョン情報への API

java.lang.Package クラスは、パッケージに関する情報を検索してアクセスするためのオブジェクトを提供します。

パッケージオブジェクトは、クラスローダで明示的に作成します。これは、パッケージでクラスを最初に定義する前に作成する必要があります。各パッケージの属性はマニフェストに保存され、クラスローダにより取得できます。

package java.lang;
public class Package {	

	// Return the name of this package.	
	public String getName(); 
	
	// Return the title of the specification of this package.	
	public String getSpecificationTitle();	
	
	// Return the version of the specification of this package.	
	public String getSpecificationVersion();	

	// Return the vendor of the specification of this package.	
	public String getSpecificationVendor();	
	
	// Return the title of the implementation of this package.	
	public String getImplementationTitle();	
	
	// Return the version of the implementation of this package.	
	public String getImplementationVersion();	
	
	// Return the vendor of the implementation of this package.	
	public String getImplementationVendor();	
	
	// Is this package is compatible with the requested version	
	public boolean isCompatibleWith(String desired);	
	
	// Get the Package for the named class	
	public static Package getPackage(String classname);	
	
	// Return the packages for currently loaded classes.	
	public static Package[] getAllPackages();	
	
	// Return true if this package is equal to another object.	
	public boolean equals(Object obj);	
	
	// Return the hashcode for this object	
	public int hashCode();	
	
	// Return the string describing this package.	
	public String toString();
} 

getName メソッドは、このパッケージの名前、たとえば、java.lang を返します。

getSpecificationTitle メソッドは、このパッケージの仕様タイトルを認識している場合はそのタイトルを、それ以外の場合は null を返します。

getSpecificationVersion メソッドは、このパッケージが実装する仕様のバージョン番号を返します。バージョンを認識していない場合は null を返します。

getSpecificationVendor は、仕様を所有する組織、グループ、またはベンダーを返します。

getImplementationTitle メソッドは、このパッケージの実装タイトルを認識している場合はそのタイトルを、それ以外の場合は null を返します。

getImplementationVersion メソッドは、このパッケージが実装する実装のバージョン番号を返します。バージョンを認識していない場合は null を返します。

getImplementationVendor メソッドは、この実装を所有する組織、グループ、またはベンダーを返します。

isCompatibleWith メソッドは、このパッケージの仕様のバージョン番号が目的のバージョン番号と互換性がある場合には true を返します。このパッケージの仕様のバージョン番号が、提供されたバージョンの文字列より大きい場合は、true が返されます。バージョンの文字列は、ピリオドで区切られた一連の正の数値です。番号は左から右へとコンポーネントごとに比較されます。番号が供給された文字列の対応する番号よりも大きい場合は、メソッドは true を返します。番号が供給された文字列の対応する番号よりも小さい場合は、false を返します。対応する番号が等しい場合は、次の番号を調べます。

getPackage メソッドは、名前でクラスのパッケージを検索します。現在のクラスローダを調査して、そのクラスローダ内でパッケージ名からパッケージオブジェクトへのマッピングを行います。メソッドは、パッケージの属性を含むパッケージオブジェクトを返します。パッケージ情報がまだロードされていない場合、あるいはクラスローダによりパッケージ情報が何も定義されていない場合は、null が返されます。

getAllPackages メソッドは、現在のクラスローダに認識されているパッケージの配列を返します。配列には、システムとクラスロードされたクラスの両方のパッケージが含まれます。クラスローダによりロードされる利用可能なすべてのパッケージを識別するわけではありません。クラスローダが情報を提供しているパッケージだけを識別します。

equals メソッドは、パッケージが、オブジェクトが渡したものと同じ名前および同じクラスローダを持っている場合は、true を返します。

hashCode メソッドは、Java 言語仕様が必要とする等価の定義と一貫性を持つハッシュコードを返します。

toString メソッドは、パッケージとパッケージ名で構成される文字列を返します。利用可能な場合には、仕様タイトルと仕様バージョン番号が文字列に付け加えられます。

1.5.6 java.lang.Class の追加

このクラスのパッケージを取得するために、java.lang.Class にメソッドが追加されました。

package java.lang;
public class Class {	
	...	
	public Package getPackage();	
	...
} 

1.5.7 java.lang.ClassLoader の追加

パッケージをサポートするために、クラスローダが拡張され、クラスからパッケージへのマッピングを追跡し、クラスローダがロードするクラスのパッケージインスタンスを定義できるように対応しています。追加されるメソッドは、サブクラスがクラスローダ内のパッケージを定義できるようにして、パッケージ実装がこのクラスローダで定義されるパッケージに関する情報を取得できるように定義します。

java.lang.Package 実装は、システムコードから現在のクラスローダを呼び出すために、そのクラスローダを識別する必要があります。

package java.lang;
public class ClassLoader {	
	...	
	// Return the non-null classloader of callers	
	public static ClassLoader currentClassLoader();	
	// Define a Package	
	protected Package(String pkgname,					
			String spectitle, String specversion,					
			String specvendor,	String impltitle,					
			String implversion, String implvendor);	
	...
} 

currentClassLoader メソッドは、システムクラスから呼び出される場合も、現在のクラスローダを検索するのに使用されます。クラスローダがロードしたクラスから呼び出される場合、このメソッドは this.getClass().getClassLoader() と同等のものを返します。この動作は、public であることを除いて、現在の SecurityManager.currentClassLoader メソッドと同じです。

保護されたアクセスの definePackage メソッドは、ロードしているクラスのパッケージを定義するのに使用されます。指定された名前を持つパッケージは、一度だけ定義され、そのパッケージの最初のクラスがロードされる前に定義されなければなりません。クラスローダは、利用可能な場合には、マニフェストからバージョン管理の属性を提供する必要があります。

1.5.8 JAR マニフェストフォーマット

現在のマニフェストフォーマットは拡張され、パッケージのバージョン情報属性の仕様に対応しました。マニフェストエントリは、Java パッケージごとに作成する必要があります。エントリ名は、パッケージのクラスおよびリソースファイルを格納するアーカイブ内のディレクトリ名になります。次に例を示します。

Manifest-version: 1.0

Name:java/util/
Specification-Title :“Java Utility Classes”
Specification-Version: “1.2”
Specification-Vendor:“Sun Microsystems Inc.”.
Package-Title:“java.util”
Package-Version: ”build57“
Specification-Vendor: “Sun Microsystems.Inc.” 

これらの属性は、マニフェストファイルのプロトタイプを作成し、jar ツールの 「-m」スイッチを使用してマニフェストの構築時に属性をマージします。今後、jar ツールはマニフェスト中でバージョン属性をブラウズおよび設定できるように拡張される予定です。

1.5.9 ユーザが実行中のパッケージを識別する方法

ユーザは、バグの発生時に、使用中のパッケージの ID をレポートできなくてはなりません。ユーザから要求されたとき、またはエラーが発生したときに、入手可能な情報をユーザに公開するかどうかは、アプリケーション、アプレット、またはブラウザにかかっています。API では以下の情報を入手してレポートできるようになっています。

1.5.10 実装のバージョン番号を識別用に限定する理由

実装はそれぞれ独自に、バグの修正、パフォーマンスの向上、または仕様の以降のリビジョンで規定された新機能の追加を目的として更新されます。パッケージは、仕様を実装すると同時に、どのバージョンの仕様を実装したかを識別する必要があります。パッケージ間のやり取りは、public および protected インタフェースおよびクラス経由でだけ行われます。public の API および動作は、時間が経過しても安定している必要があります。そのため、あるパッケージの実装の変更が、別のパッケージの動作に影響を与えないように注意する必要があります。

パッケージのクラスが常に仕様を忠実に実装している場合には、仕様を識別するだけで十分です。しかし現実にはこのようなことはまれなので、バグに関係している可能性のあるパッケージが報告されるために、パッケージは自らの身分を明らかにする必要があります。

実装のバージョン識別子に何らかの意味を持たせるのには、重要な目的があります。バグを追跡するのが目的である場合、固有の番号を付ければ十分目的を果たすことができます。また、クライアントのパッケージがベンダーのパッケージの特定のバージョンに含まれるバグを回避する場合にも、固有の番号を付けると効果的です。番号のバージョンをテストし、バグを回避できるからです。

ただし、1 つのパッケージがほかのパッケージに含まれるバグを回避しようとすると、多くの付加的な問題が発生することがあります。ほかのパッケージは、仕様の一部ではない動作を識別する必要があり、実装の一部にだけ属する動作を使用しようとするかもしれません。そのような実装固有の動作は、開発者によって確認およびテストされた特定のバージョンでないかぎり、信用することはできません。

ベンダーパッケージのあるバージョンで初めて発生したバグは、次のバージョンでも障害になる場合と、ならない場合とがあります。バグを含むパッケージのクライアントが、バージョン番号に基づくバグの回避を行うと、特定のバージョンのバグを正しく回避できる場合があります。バグを含むパッケージが修正された場合、クライアントパッケージはバグが修正されたかどうかをどのようにして知ることができるのでしょうか。あとのバージョンにもバグが含まれていると仮定した場合、クライアントは依然としてバグを回避する処置をとらなければなりません。バグを含まないパッケージでは、バグの回避そのものが正しく機能しない可能性があります。このため、バグの修正により一連のバグが発生する可能性があります。新しいバージョンをテストすることにより、バグの回避が必要か、あるいはバグの回避が正常に動作しているパッケージに問題を発生させることになるのかを見極めることができるのは、開発者だけです。また、あるバージョンにバグが存在することを知っているのは、開発者だけです。

1.6 開発方法の文書化

ここでは、製品の開発と配布のそれぞれの側面について説明し、頑強かつ更新可能な製品を実現する方法についての方向付けを示します。

 


目次 | 前の項目 | 次の項目 JavaTM 製品のバージョン管理の仕様