2015年7月25日土曜日

正しくカスタムプロトコルのURLStreamHandlerを登録する

JavaでカスタムプロトコルのURLStreamHandlerを登録する方法として、ネット上で挙げられている方法はURL.setURLStreamHandlerFactoryである。
しかし、このメソッドは大きな欠点があり、複数回setURLStreamHandlerFactoryを呼びさせない仕様にしてある。つまり、複数のURLStreamHandlerFactoryが登録できない。複数のURLStreamHandlerを登録したいときには、一つのURLStreamHandlerFactoryで、すべてのURLStreamHandlerを登録しなければならない。

しかし、ドキュメントをよく読むと、カスタムプロトコルのURLStreamHandlerを登録する方法がもう一つ存在すると示されている。
  1. 以前にアプリケーションがURLStreamHandlerFactoryのインスタンスをストリーム・ハンドラ・ファクトリとして設定している場合は、そのインスタンスのcreateURLStreamHandlerメソッドがプロトコル文字列を引数として呼び出されて、ストリーム・プロトコル・ハンドラを作成する。
  2. まだURLStreamHandlerFactoryが設定されていない場合、あるいはファクトリのcreateURLStreamHandlerメソッドがnullを返した場合は、コンストラクタが次のシステム・プロパティの値を探す。
    java.protocol.handler.pkgs
    このシステム・プロパティの値がnullでなければ、値は、垂直スラッシュ文字「|」で区切られた、パッケージのリストとして解釈される。コンストラクタは次の名前を持つクラスをロードしようとする。
    <package>.<protocol>.Handler
    ここで、<package>にはパッケージの名前が入り、<protocol>にはプロトコルの名前が入る。このクラスが存在しない場合、あるいはクラスは存在してもそれがURLStreamHandlerのサブクラスではない場合には、リストにある次のパッケージを試すことになる。
  3. 以上の手順でもプロトコル・ハンドラが見つからなかった場合、コンストラクタはシステムのデフォルト・パッケージからロードしようとする。
    <system default package>.<protocol>.Handler
    このクラスが存在しない場合、またはクラスは存在するがURLStreamHandlerのサブクラスではない場合は、MalformedURLExceptionがスローされる。
2番目のシステムプロパティjava.protocol.handler.pkgsを使う方法である。システムプロパティにパッケージ名を登録すると、そのパッケージ配下から必要なURLStreamHandlerを検索する。パッケージ名はパイプ区切りで列挙できるので、この方法を使えば、何度でもURLStreamHandlerを登録することができる。

具体的な方法は、まず<任意のパッケージ名>.<プロトコル名>.HandlerのクラスとしてURLStreamHandlerのサブクラスを作る。
システムプロパティjava.protocol.handler.pkgsに対して、<任意のパッケージ名>を登録する。このとき、java.protocol.handler.pkgsがすでに登録されているときは、パイプ区切りで追加する必要があるので、以下のように分岐をして判別する必要がある。
String pkg = System.getProperty("java.protocol.handler.pkgs", "");
if(!"".equals(pkg)) pkg += "|";
pkg += "<任意のパッケージ名>";
System.setProperty("java.protocol.handler.pkgs", pkg);

これだけで、複数の必要なカスタムプロトコルのURLStreamHandlerを登録できる。