デベロッパー

デベロッパー

Java Web サービスで Restlet ルーティングシステムを活用する (1/2)

Jerome Louvel, Thierry Boileau
2010年7月23日 / 10:00
 
 
本稿の出典は Jerome Louvel と Thierry Boileau の共著による「Restlet in Action」(Manning Publications; ISBN:9781935182344)。両者がRestlet アプリケーションの概要を示し、 Restlet ルーティングシステムの2つの重要な要素である Restlet のフィルタとルータを紹介する。彼らはフィルタがどのようにしてコールの前後処理を円滑にし、ルータがどのようにして利用可能な複数のターゲットルートへのコールのディスパッチを可能にするかを示している。


Restlet アプリケーションには重要な目的が複数あるため、これらがどのように構築され、その後、実行時に Restlet プロジェクトによってどのように利用されるのかを理解することは絶対不可欠だ。図1では、最も一般的で再利用可能なものから中心の最も具体的なものへ向かい、全体のデザインを3つの同心円層で示す。  

Restlet アプリケーション構造
図1. Restlet アプリケーションは3つの同心円層に構造化されており、インバウンド/アウトバウンドコールを論理的な手順で処理する。


アプリケーションは、矢印を使って描写されたインバウンド・サーバコールとアウトバウンド・クライアントコールの両方を処理することができる。

Restlet アプリケーションのインバウンドコールとアウトバウンドコール

インバウンドコールは、アプリケーションに含まれるすべてのリソースに共通のサービスフィルタリング・レイヤを最初に通る。このレイヤでは、圧縮表示の自動デコードや、一部表示のサポートなどができる。 コールは次に、認証などのために独自のフィルタリングをかけたり、リクエストを主にその URI に基づいてターゲットのリソースにルーティングするユーザールーティングレイヤを通る。最後に、コールはリソースハンドリングレイヤに到達して、そこでターゲットのリソースがリクエストを処理し、同じ経路でオリジナルのクライアントに戻されるレスポンスで応答する。ここで概説したリクエスト処理の流れの次には与えられたコールを担当する Java スレッドが1つ来る。

さらに、クライアントコールはサーバリソース内でサーバコールを処理しているときにクライアントリソースを作成することでアプリケーションから呼び出されるのが一般的だ。クライアントリソースは、(一般的にはリモートホスト上の)ターゲットリソースに到達する前に上位レイヤ経由でリクエストを送信し、最後はレスポンスと一緒に戻ってくる。上位のサービスフィルタリング・レイヤがレスポンスとして受け取った表示の自動伸張といったサービスを提供できるようになる一方で、ユーザールーティング・レイヤは自動認証などを処理するためにリクエストのフィルタリングやディスパッチができるようになる。

Restlet ルーティングシステム

ユーザールーティング・レイヤはかなり制限を受け、インカミングコールを追跡するシンプルな Restlet サブクラスだけを提供する場合がある。IP アドレスをブロックしたり、URI に基づいてコールをターゲットのサーバリソースにルーティングするフィルタはどうすれば構築できるのだろうか? 

本稿は Restlet ルーティングシステムを紹介して、これらの質問に答える。ルーティングシステムにとって重要な2つの要素である Restlet フィルタとルータを中心に紹介していく。前者はコールの前・後処理を円滑に処理し、後者は利用可能な複数のターゲットルートの1つに対するコールのディスパッチを可能にするが、これは通常、ルーティングされるコールのターゲット URI に基づいて行われる。

フィルタ付き前・後処理をコール


サーバ 側ではコールを受け取った後、そしてクライアント側ではコールを送信する前に、ターゲットになるコールのリソース URI に基づいて大きく違うことのない何らかの動作をシステム的に適用することが一般的となっている。このような処理要素に与えられる一般的な名前が「フィルタ」だ。本来、Restlet Framework はorg.restlet.Restletのサブクラスである抽象的なorg.restlet.routing.Filterクラスを提供する。 


Restlet サブクラスはマルチスレッド
Restlet」 サブクラスは同時コールのサポートが必須であり、完全にスレッドセーフでなければならない点が重要だ。複数のスレッド(コールごとに1つずつ)によって実行されたときに適切に動作するコードを書くことは容易ではなく、詳細に関しては「Java Concurrency in Practice」の一読を強く推奨する。

図2は、同じ「Filter」インスタンスによって処理される3つの同時コール(A、B、および C)を示している。各スレッドがコール(リクエストとレスポンスの組み合わせ)をサポートし、次の Restlet に行くためにフィルタのトラバースを試みる。場合によってはコールがフィルタによってブロックされ、スレッドが次の Restlet に行かずに即座に戻ることもある。「ストップ」サインで示されるように、コール B はフィルタによって停止されている。  

Restlet フィルタ
図2. 3つの同時コールを処理し、そのうち2つを次の Restlet に送って3つ目をブロックするフィルタ。


すべての Restlet サブクラス同様、「Filter」のエントリーポイントが最終的に「Uniform」 インターフェースで宣言される「handle (Request, Response) 」メソッドになる。このメソッドは3段階で機能する。
  1. まず、 「beforeHandle (Request, Response) 」メソッドを呼び出し、それがフィルタにコールの前処理を行わせる。
  2. このメソッドは、処理を次の Restlet に進めるか、スキップするか、あるいは即座に停止してスレッドスタックを1つ上に戻るかどうかを示す結果フラグを返す。最初のケースでは、「doHandle (Request, Response) 」メソッドを呼び出すことで、アタッチしている次の Restlet をフィルタが呼び出す。Restlet が全くアタッチしていない場合は、レスポンスに「server internal error」(HTTP ステータス500)がセットされる。
  3. 次の Restlet が返るか、前処理が次の Restlet をスキップするよう求める場合は「afterHandle (Request, Response) 」メソッドが呼び出され、フィルタがコールの後処理を実行できるようになる。後処理は一般的に、受け取ったレスポンスを基本にして HTTP GET メソッドで返った表示を圧縮するなどといったことを行う。
ここで、IP アドレスに基づいてブロックを行うフィルタを実現するリスト1の具体例を見たい。「Set < String>」インターフェースの専用スレッドセーフ・インプリメンテーションを使い、ブロックされる IP アドレスのリストを格納していることが分かるだろう。

リスト1. IP アドレスブロックフィルタ
public class Blocker extends org.restlet.routing.Filter {

    private final Set<String> blockedAddresses;                         #A

    public Blocker(Context context) {
        super(context);
        this.blockedAddresses = new CopyOnWriteArraySet<String>();
    }

    @Override                                                          
    protected int beforeHandle(Request request, Response response) {    #B
        int result = STOP;

        if (getBlockedAddresses()
                .contains(request.getClientInfo().getAddress())) {
            response.setStatus(Status.CLIENT_ERROR_FORBIDDEN,
                    "Your IP address was blocked");
        } else {
            result = CONTINUE;
        }

        return result;
    }

    public Set<String> getBlockedAddresses() {
        return blockedAddresses;
    }

}
#A ブロックされる IP アドレスのリスト
#B クライアント IP アドレスとブロックされるアドレスのリストを比較する前処理メソッド

このフィルタをテストするには、このアプリケーションをアップデートする必要がある。特に、リスト2と同じロジックが含まれるものの「Restlet」を拡張する別のクラスとしてパッケージングされる「Tracer」クラスの代わりに「Blocker」フィルタのインスタンスを返すcreateInboundRoot () メソッドだ。

リスト2. アプリケーションにインバウンドルート Restlet を提供する
import org.restlet.Application;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;

public class MailServerApplication extends Application {
   
    public static void main(String[] args) throws Exception {     #A
        Server mailServer = new Server(Protocol.HTTP, 8182);
        mailServer.setNext(new MailServerApplication());
        mailServer.start();
    }

    @Override
    public Restlet createInboundRoot() {                          #B
        return new Restlet() {
            @Override
            public void handle(Request request, Response response) {
                String entity = "Method       : " + request.getMethod()
                        + "\ nResource URI : " 
                        + request.getResourceRef()
                        + "\ nIP address   : "
                        + request.getClientInfo().getAddress()
                        + "\ nAgent name   : "
                        + request.getClientInfo().getAgentName()
                        + "\ nAgent version: "
                        + request.getClientInfo().getAgentVersion();
                response.setEntity(entity, MediaType.TEXT_PLAIN);
            }
           };
    }
   }
#A HTTP サーバと一緒にアプリケーションを起動する。
#B ルート Restlet を作成してリクエストをトレースする。

リスト3では、フィルタが新しいインバウンドルートとして返り、「setNext (Restlet)」メソッドを使って「Tracer」インスタンスにアタッチされる様子が分かる。 

リスト3. IP アドレスブロックフィルタ
@Override
    public Restlet createInboundRoot() {
        Blocker blocker = new Blocker(getContext());
        // blocker.getBlockedAddresses().add("127.0.0.1");
        blocker.setNext(new Tracer(getContext()));
        return blocker;
    }


われわれのフィルタをテストするには、アップデートされたアプリケーションを起動して、ブラウザで http://localhost:8182/test/abcd URI に飛ぶ。こうすると、先のリストと同じ結果が返るはずだ。ここで上のメソッドの2行目のコメントを外し、アプリケーションを再起動する。ブラウザページをリフレッシュすると、今度はフィルタが機能したことを示す「Your IP address was blocked」というメッセージが表示されるはずだ。
【関連記事】
ブルーコート、IDC 調査の国内 WAN アプリケーション配信市場でシェアトップに
音楽出版社と Eagle がスマートフォン向けアプリで業務提携
OKI、世界初の ZigBee テレコムサービスプロファイル認証を獲得
ノベル、ログ管理製品の最新版をソフトウェアアプライアンスで販売
米 IBM、中国の武漢に新グローバルデリバリーセンターを開設

New Topics

Special Ad

ゆりかごからロケットまで、すべての乗り物をエンジョイ
ゆりかごからロケットまで、すべての乗り物をエンジョイ えん乗り」は、ゆりかごからロケットまで、すべての乗り物をエンジョイする、ニュース、コラム、動画などをお届けします! てんこ盛りをエンジョイするのは こちらから

Hot Topics

IT Job

Interviews / Specials

Popular

Access Ranking

Partner Sites