Restlet + Tomcatで静的ファイルへのアクセスを可能にする

Restlet 1.0.11をサーブレットコンテナ(今回はTomcat 5.5)上で動かしている環境で、静的なファイルへのアクセスを可能にする手段について。自分で書いておいて何だが、恐ろしく需要がない記事になりそうだ
RestletのTutorialに、『Serving static files』という、そのまんまな記述がある。次のコードは、Tutorialに記載されているもの。

// Create a component
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
component.getClients().add(Protocol.FILE);

// Create an application
Application application = new Application(component.getContext()) {
    @Override
    public Restlet createRoot() {
        return new Directory(getContext(), ROOT_URI);
    }
};

// Attach the application to the component and start it
component.getDefaultHost().attach("", application);
component.start();

このように、Directoryを利用すれば、静的ファイルへのアクセスも簡単に実装できる、というわけである。

以上。

…となると思っていたのになぁ。

Tutorialのサンプルコードは、Restletが持っているシンプルなHTTPサーバ上で動かすようになっている。しかし、現実の開発ではTomcatなりJettyなりといった、著名なサーブレットコンテナに乗せるのが普通だろう。そして、サーブレットコンテナに乗せて動かす方法も、ちゃんとFAQに載っている。大ざっぱに説明すると、web.xmlにcom.noelios.restlet.ext.servlet.ServerServletおよびそれと連携させるRestletのApplicationを書き込めばよい。

Applicationは、きっとこんな感じで書けばよいのだろう、という見当がつく。

public class RestfulApplication extends Application {

    public RestfulApplication(Context parentContext) {
        super(parentContext);
    }

    @Override
    public synchronized Restlet createRoot() {
        return new Directory(getContext(), "file:///path/to/dir");
    }
}

これで、例えばhttp://localhost:8080/myservice/album/photo.jpgにアクセスすると、file:///path/to/dir/album/photo.jpgを持ってきてくれるはずである。しかし、これが404 Not Foundになってしまう。ログを見た感じでは、URIの変換は正常に行われているのだが。

原因は、クライアントコネクタが不足していることにある。ローカルファイルへのアクセスを可能にするには、それ用のコネクタを登録しなければならない。Tutorialのコードでいう、次の処理を行わなければならないのである。

Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
component.getClients().add(Protocol.FILE); // ココ!!

ところが、ApplicationからComponentを取得する手段は無い*1。さて、どうしたものか。

いろいろ調べていて、次のログを発見した。

http://osdir.com/ml/java.restlet/2007-05/msg00010.html

多少トリッキーな実装になるが、次のようにServerServletを拡張して手を加えるしかないようだ。

public class ExtendedServerServlet extends ServerServlet {

    private boolean firstCall = true;

    @Override
    public HttpServerHelper createServer(HttpServletRequest request) {
        
        HttpServerHelper result = super.createServer(request);
        
        if(firstCall) {
            getComponent().getClients().add(Protocol.FILE);
            firstCall = false;
        }
        
        return result;
    }
}

これを、ServerServletの代わりに使う。

	<servlet>
		<servlet-name>RestletServlet</servlet-name>
		<servlet-class>
			my.app.package.ExtendedServerServlet
		</servlet-class>
	</servlet>

これで静的ファイルへのアクセスが可能になる。

こんなやり方しないとクライアントコネクタを初期化できないのはダメだろう、ということで、この問題はBTSに登録されている。しかし、ターゲットマイルストーンがどんどん遠のいている(現在、2.0 M2!)ので、いつ対処されるか見当もつかない。

たぶん、Apacheをフロントエンドに立てて、静的ファイルへのアクセスは全てそちらが受け持つ、というやり方をすればいいのだろう。そうすれば、この問題にぶつからずに済むし、きっとパフォーマンスもすぐれている。しかし、ルーティング設定を分散させるのもなんかすっきりしないなぁ、と思ったり。

*1:あるにはあるが、トリッキーすぎる処理になる