Prev / Next / たまにっき。

Java サービスプロバイダ

Category: [Java][Tips]
2006-11-08

jar ファイルにはサービスの実装を定義する項目がある.META-INF/services ディレクトリに置かれているファイルを基に,サービスの実装をユーザプログラムなしに,アプリケーション側に登録することが可能になる機構である.
例えば Image I/O や Midi,JNDI などで使われているようだ.クラスパスを通しておくだけで実装クラスが得られるので便利っぽい.

この機構の使い方は META-INF/services/ ディレクトリ以下に SPI のインターフェース名のファイルを作成し,そのファイルの中に実装クラスの名前を書く.こうすることでユーザプログラム側で特別な処理なしに,特定のクラス経由で実装クラスの Class オブジェクトを得ることができる.
しかし,特定のクラスというのがはっきりしない.jar ファイル仕様のサービスプロバイダの項目では例として以下のコードが掲載されている.

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;
}


このようなコードで CharCodec SPI の実装クラスを得られるのだ.しかし,Java SE 5 には Service というクラスは存在しない.この機構を実際に使うときにはどのクラスを使えばよいのかがわからなかったので調べてみた.「こうすべき」という正式な文書は見つからなかったものの,まぁできたので良しとする.

結論としては javax.imageio.spi.ServiceRegistry を使う.Image I/O は使わないのに,imageio パッケージを使うのは何とも気持ち悪いが他に適当なクラスも見つからなかったので仕方ないか.

以下,サンプルプログラム


SPI: HogeSpi
SPI 実装クラス: MyHogeService
クライアント: Client

HogeSpi が Hoge を作成し Hoge インターフェースの hoge メソッドを実装する.

HogeSpi.java

package com.oikaze.spitest.spi;
import com.oikaze.spitest.Hoge;
public interface HogeSpi{
    public Hoge createHoge(String category);
}



Hoge.java

package com.oikaze.spitest;
public interface Hoge{
    public void hoge();
}



MyHogeService.java

package com.oikaze.spitest;
import com.oikaze.spitest.spi.HogeSpi;
public class MyHogeService implements HogeSpi{
    public Hoge createHoge(final String category){
        return new Hoge(){
            public void hoge(){
                System.out.println("Hello " + category);
            }
        };
    }
}



Client.java

package com.oikaze.spitest;
import java.util.Iterator;
import com.oikaze.spitest.spi.HogeSpi;
import javax.imageio.spi.ServiceRegistry;
public class Client{
    public Client(String label){
        for(Iterator<HogeSpi> i = ServiceRegistry.lookupProviders(HogeSpi.class); i.hasNext(); ){
            Hoge hoge = i.next().createHoge(label);
            hoge.hoge();
        }
    }
    public static void main(String[] args){
        String label = "World";
        if(args.length >= 1) label = args[0];
        new Client(label);
    }
}



META-INF/services/com.oikaze.spitest.spi.HogeSpi

com.oikaze.spitest.MyHogeService



ServiceRegistry の lookupProviders に SPI インターフェースの Class オブジェクトを渡すとその実装クラスの iterator が返される.この例では MyHogeService 一つだけ.META-INF/services/ 以下のファイルで MyHogeService を登録しているから.

プロジェクト一式 (maven2)
バイナリjarファイル

Category: [Java][Tips]