第 13 章: サービスプロバイダインタフェースの概要


注:

バージョン 5.0 の場合、新規コンストラクタの AudioFormatAudioFileFormat、および MidiFileFormat により、さらに高度な形式での記述が可能になっています。


サービスとは

サービスとは、アプリケーションプログラムで JavaTM Sound API の実装を使用すると自動的に利用可能になるサウンド処理機能の総称です。サービスを構成するのは、オーディオデータと MIDI データの読み込み、書き込み、ミキシング、処理、変換の各作業を行うオブジェクト群です。Java Sound API の実装では、通常は、基本サービスセットが提供されていますが、さらに、API に新しい機構も組み込まれて、サードパーティ開発者 (または実装自体のベンダー) を対象にした新しいサウンドサービスの開発もサポートされています。これらの新しいサービスを、既存の導入済み実装に「プラグイン」の形で組み込むと、新規リリースとしなくても機能を拡張することができます。Java Sound API アーキテクチャは、サードパーティサービスをシステムに統合する方法として、アプリケーションプログラムのサードパーティサービスへのインタフェースを「ビルトイン (組み込み)」サービスへのインタフェースと同じにしました。場合によっては、javax.sound.sampled パッケージや javax.sound.midi パッケージを使用するアプリケーション開発者が、サードパーティサービスを使用していることに気づかないこともあります。

サードパーティのサンプリングオーディオサービスの例として、次のようなものが挙げられます。

サードパーティの MIDI サービスは、次のようなものです。

サービスの動作

javax.sound.sampled パッケージおよび javax.sound.midi パッケージは、アプリケーションにサウンドサービスを付加することを考えているアプリケーション開発者に必要な機能を提供するものです。この 2 つのパッケージはサウンドサービスの「消費者 (コンシューマ)」を対象に、オーディオ、MIDI の両サービスに関する情報の取得、制御、およびアクセスに必要なインタフェースを提供します。また、Java Sound API は、サウンドサービスの「提供者 (プロバイダ)」を対象に、抽象クラスを定義するためのパッケージであるjavax.sound.sampled.spijavax.sound.midi.spi を提供しています。

新しいサウンドパッケージの開発者は、SPI パッケージの中の、使用するクラスの具象サブクラスを実装します。これらの具象サブクラスと新しいサービスのサポートに必要な任意のサブクラスは、含まれているサービスの説明とともに、JavaTM Archive (JAR) アーカイブファイルに置かれます。この JAR ファイルがユーザの CLASSPATH にインストールされると、実行システムは、自動的に新しいサービスを利用可能にして、JavaTM プラットフォームの実行システムの機能性を拡張します。

インストールされた新しいサービスには、すでにインストールされているほかのサービスと同様にアクセスできます。サービスの消費者は、新しいサービスに関する情報、または新しいサービスクラス自体のインスタンスを取得することができます。そのためには、AudioSystem クラスと MidiSystem クラスのメソッド群 (前者は javax.sound.sampled パッケージ、後者は javax.sound.midi パッケージに収録) を呼び出して、新しいサービスに関する情報、あるいは新規または既存のサービスクラス自体のインスタンスを戻します。アプリケーションプログラムはインストールされたサービスを利用するために、SPI パッケージ内のクラスおよびそのサブクラスを直接参照する必要はないので、直接参照すべきではありません。

たとえば、Acme Software, Inc. という架空のサービスプロバイダが、アプリケーションプログラムで新しい形式のサウンドファイル (ただし、オーディオデータは標準のデータ形式) を読み込むパッケージを提供できないかと考えたとします。SPI クラス AudioFileReader は、たとえば AcmeAudioFileReader というクラスにサブクラス化できます。新しいサブクラスで、Acme は AudioFileReader に定義されているすべてのメソッドの実装を提供します。この場合は、getAudioFileFormatgetAudioInputStream の 2 つのメソッド (引数バリアント付き) だけです。次に、アプリケーションプログラムが読み込もうとしたサウンドファイルが Acme のファイル形式だった場合は、javax.sound.sampled 内の AudioSystem クラスのメソッドを呼び出してファイルとそのファイル情報にアクセスします。AudioSystem.getAudioInputStream メソッドと AudioSystem.getAudioFileFormat メソッドは、オーディオストリームを読み込む標準 API を提供します。AcmeAudioFileReader クラスがインストールされている場合は、このインタフェースは拡張され、新しいファイルタイプを透過的にサポートします。アプリケーション開発者は、新しく登録した SPI クラスに直接アクセスする必要はありません。AudioSystem オブジェクトのメソッドが、インストールされている AcmeAudioFileReader クラスについてのクエリーを渡します。

これらの「ファクトリ」クラスを持つことの利点は何でしょうか。また、新しく提供されるサービスへの直接アクセスをアプリケーション開発者に許可しない理由は何でしょうか。直接アクセスも可能性としては考えられますが、オブジェクトにサービスの管理とインスタンス化をすべてパススルーするゲートキーパーシステムが備わっているため、アプリケーション開発者は、インストールされている各サービスについて知る必要がありません。アプリケーション開発者は、自覚することさえなく、自分にとって使用価値のあるサービスのみを使用します。同時に、このアーキテクチャにより、サービスプロバイダはパッケージ内の利用可能なリソースを効率的に管理することができます。

新しいサウンドサービスの使用は、多くの場合、アプリケーションプログラムからは見えません。たとえば、アプリケーション開発者がファイルからオーディオのストリームで読み込む場合を考えます。thePathName によりオーディオ入力ファイルを識別すると仮定すると、プログラムは次のようになります。

    File theInFile = new File(thePathName);
AudioInputStream theInStream = AudioSystem.getAudioInputStream(theInFile);
バックグラウンドで、AudioSystem は、ファイルを読み込むことができるインストールサービスを判別し、そのサービスに対して、データを AudioInputStream オブジェクトとして提供するよう要請します。開発者は、入力オーディオファイルが新しいファイル形式 (Acme 形式など) になっていて、インストールされているサードパーティサービスでサポートされていることを知る必要も気にする必要もありません。プログラムは、最初に AudioSystem オブジェクトを介してストリームにアクセスします。その後、ストリームとプロパティには AudioInputStream のメソッドを介してアクセスします。これらはどちらも javax.sound.sampled API の標準オブジェクトであり、新しいファイル形式に必要な特別な処理は、完全に隠されています。

プロバイダが新しいサービスを準備する方法

サービスプロバイダは新しいサービスを特別な形式の JAR ファイルで提供します。このファイルは、Java ランタイムが JAR ファイルを検索するユーザのシステム上のディレクトリにインストールされます。JAR ファイルはアーカイブファイルで、各ファイルにはアーカイブ内で階層ディレクトリ構造に編成されているファイルセットが含まれます。これらのアーカイブに保存されるクラスファイルの準備方法の詳細については、第 14 章と第 15 章のオーディオパッケージと MIDI SPI パッケージの仕様を参照してください。ここでは、JAR ファイルの作成プロセスの概要を説明します。

新しいサービスに使用する JAR ファイルには、その JAR ファイルでサポートされる各サービス用のクラスファイルが含まれていなければなりません。Java プラットフォームの規約に従い、各クラスファイルは新しく定義されたクラスの名前を持ちます。この新しく定義されたクラスは、サービスプロバイダの抽象クラスのうちのいずれかの具象サブクラスです。JAR ファイルには、新しいサービスの実装に必要なサポートクラスも含める必要があります。また、実行システムのサービスプロバイダ機構で新しいサービスの位置を特定できるようにするため、JAR ファイルには、定義されている新しいサブクラスに SPI クラス名をマップする特殊ファイル (次に説明) も含める必要があります。

前述の例の続きで、Acme Software, Inc. は新しいサンプリングオーディオサービスのパッケージを配布しているとします。このパッケージは、次の 2 つの新しいサービスにより構成されるものとします。

まず、ビルドを行う /devel という名前のディレクトリの下にサブディレクトリ群を作成し、そこに新しいクラスファイルを置きます。新しいクラスを参照できるパス名を使用して編成する必要があります。

    com/acme/AcmeAudioFileReader.class
com/acme/AcmeAudioFileWriter.class
さらに、サブクラス化されている新しい各 SPI クラス用に、META-INF/services という特別な名前のディレクトリにマッピングファイルを作成します。このファイル名はサブクラス化されている SPI クラスの名前で、ファイルには、その SPI 抽象クラスの新しいサブクラスの名前が含まれています。

次の内容で構成される

META-INF/services/javax.sound.sampled.spi.AudioFileReader ファイルを作成します。

# Providers of sound file-reading services
# (a comment line begins with a pound sign)
com.acme.AcmeAudioFileReader

および、次の内容で構成される

META-INF/services/javax.sound.sampled.spi.AudioFileWriter ファイルを作成します。

   # Providers of sound file-writing services
com.acme.AcmeAudioFileWriter

ここで、任意のディレクトリから、次のコマンド行により jar を実行します。

jar cvf acme.jar -C /devel .
-C オプションにより、jar はコマンドが実行されたディレクトリを使用しないで、/devel ディレクトリを使用します。最後の引数のピリオドは、そのディレクトリ (/devel) の内容をアーカイブするよう jar に指示します。ただし、ディレクトリそのものはアーカイブしません。

これにより、次の内容の acme.jar ファイルが作成されます。

com/acme/AcmeAudioFileReader.class
com/acme/AcmeAudioFileWriter.class
META-INF/services/javax.sound.sampled.spi.AudioFileReader
META-INF/services/javax.sound.sampled.spi.AudioFileWriter
META-INF/Manifest.mf
jar ユーティリティ自体により生成される Manifest.mf ファイルは、アーカイブに含まれるすべてのファイルのリストです。

ユーザが新しいファイルをインストールする方法

アプリケーションプログラムから新しいサービスにアクセスしようとする一般のユーザまたはシステム管理者は、インストールを簡単に行うことができます。提供された JAR ファイルを CLASSPATH 内のディレクトリに置きます。実行中に、Java ランタイムは、必要に応じて参照クラスを検出します。

同じサービスに複数のプロバイダをインストールするのは誤りではありません。たとえば、2 つの異なるサービスプロバイダが同じタイプのサウンドファイルの読み込みをサポートする場合があります。このような場合、システムはいずれかのプロバイダを任意に選択します。選択するプロバイダをユーザが決める場合は、そのプロバイダのみをインストールします。