既存のウェブアプリケーションと連携するアプリケーションを作成する。その2

はじめに。

前回はjava.net.Authenticatorを使うことで、ユーザ名とパスワードを使った認証をすることができることを確認しました。しかし、java.net.Authenticatorはスタティックメソッドによって設定され、VMに対してユニークです。ですので、前回のような形でユーザ名とパスワードを設定してしまうと、異なるユーザ名とパスワードの組を要求するサイトへの接続を行う前に、setDefault()をやり直さなければいけません。しかし、サーブレットなどからユーザ認証を含むURLを開く場合など、単純に、setDefault()⇒URL.openConnection()という手順を踏むと問題が出てきます。というのは、サーブレットは原則マルチスレッドで動作しているからです。ユーザAのためのユーザ名とパスワードをsetDefault()で設定した後すぐにコンテクストスイッチが発生し、ユーザBのためのopenConnection()が行われるとすると、大変まずいことになります。一連の作業を同期してしまうのは良いアイディアですが、IOのボトルネックを作ってしまいますので、できれば避けたいところです。ではどうしたらよいのか?それが今回のお題です。

Authenticator.getRequestingURL()と、URL.getUserInfo()を組み合わせる。

AuthenticatorにはgetRequestingURL()というメソッドがあります。これはユーザ認証を要求しているURLオブジェクトを返すというものです。ということは、このURLオブジェクトに何とかしてユーザ名とパスワードの情報を持たせることができれば、その情報を元にPasswordAuthenticationオブジェクトを作成することができるでしょう。
一つ思いつくのは、URLクラスを継承した独自のクラスを定義し、そのクラスにユーザ名とパスワードの属性を持たせてしまうということです。これはオブジェクト指向設計としてはアリですが、JAVAではナシです。というのは、java.net.URLクラスはfinalであると宣言されており、URLクラスを継承したクラスを定義することができないのです。
そこで、URL.getUserInfo()メソッドを使います。前回、http://user:pass@host:portの形式を指定してもユーザ認証をしてくれないと嘆きましたが、この形式を使うことでURLオブジェクトからAuthenticatorにログインユーザ名とパスワードを渡すことができます。


URL url = new URL("http://user1:pass1@some.where.requries.auth:1234/");
これで、ユーザ名とパスワードの情報(UserInfo)を持ったURLができました。ユーザ情報を得るには、URL.getUserInfo()メソッドを使います。この例では、getUserInfo()メソッドを使うとStringの値として"user1:pass1"という値が返されます。あとはこれを使って、PasswordAuthenticationオブジェクトを生成して返してやればよいということになります。