気になる開発プロダクツ

第3回Restlet 1.0.1-RESTアプリケーションを手軽に実現するフレームワーク

Restletとは?

Restlet(http://www.restlet.org/)は、Web APIなどで主流となっているREST(REpresentational State Transfer)型の通信を行うアプリケーションを構築する「軽量な(Lightweight)」Javaフレームワークです。CDDL1.0とGPL2.0のデュアルライセンスの下で配布されています。開発はフランスのNoelios Consulting社(http://www.noelios.com/:フランス語)が主体となって行っています。バージョン1.0.1がリリースされたのは2007年5月3日です。

JavaのREST APIといえば、JAX-RS(JSR 311)の仕様をJCPで詰めている最中ですが、Restletではバージョン2.0のAPIをJCPに提出することを計画しています(2007年4月25日付のNoeliosのブログより)。

なぜRestletという名称なのかについて、開発者による記述を見つけることはできませんでしたが、おそらくサーブレット(Servlet)やアプレット(Applet)と同じように、RESTに「小さいもの」を表す接尾辞の-letをつけることで、RESTアプリケーションを手軽に開発できるフレームワークにしたいという意味を込めているのでしょう。

RestletによるRESTアプリケーション

REST自体のことについては、別のWebサイトや書籍などに説明を委ねることにして、Restletにより構築できるアプリケーションとはどんなものなのか、さっそく短いメッセージを出力する(いわゆるHello World)クラスをリスト1に示します。これを含め、本稿で紹介するリストはJDK 6u1の環境で実行を確認しています。

リスト1 短いメッセージを出力するRESTアプリケーションの例
Restlet restlet = new Restlet()  {
  @Override
  public void handle( Request request, Response response )  {
    response.setEntity(
      new StringRepresentation(
        "はじめてのRestlet", MediaType.TEXT_PLAIN,
        new Language( "ja" ), new CharacterSet( "Shift_JIS" )
      )
    );
  }
};

表示領域の関係でネストが深くなっていますが、整理して説明しましょう。

RestletによるRESTアプリケーションは、Restletクラスのhandle()メソッドをオーバーライドしたサブクラスとして構築できます。サーブレットがHttpServletクラスのdoGet()やdoPost()の各メソッドをオーバーライドするのと似ています。handle()メソッドの引数Requestと同じく、ResponseはサーブレットのServletRequestおよびServletResponseと意味は似通っていますが、データを設定したり取得したりする方法が異なります。

レスポンスとして返すデータはResponse#setEntity()メソッドで設定します。日本語を含まない半角テキスト(ASCIIコード)であれば、このメソッドで直接テキストを設定できますが、日本語を含むテキストの場合は、文字化けを防ぐためにStringRepresentationやCharacterSetなどのクラスを使っています。

レスポンスには、XML、ファイル、シリアライズ可能なオブジェクトなどを設定することができます。クラスやメソッドの詳細は、バージョン1.0のAPIドキュメントを参照してください。

サーバの設定と起動

RESTアプリケーションは、その性格上、サーバ内で動作させる必要があります。RestletではServerクラスが用意されていますので、それを設定しておきます。設定といっても、リスト2のとおりインスタンスを生成し、start()メソッドでサーバを起動させるだけです。起動後はクライアントからのアクセス待ちになるため、停止するにはCtlr+Cを押すか、このプロセスを停止させます。設定ファイルやTomcatなどのアプリケーションサーバを別に用意しなくても良いところが「軽量さ」をよく表しています。

リスト2 RESTアプリケーションを動作させるサーバの設定と起動の例
Server server = new Server( Protocol.HTTP, 8888, restlet );
server.start();

Serverクラスのコンストラクタには、使用するプロトコルの種類、ポート番号、動作させるRESTアプリケーションを引数として与えます。これによって、設定とデプロイが完了することになります。

このロジックは一般的なJavaクラスの中で記述できますので、実際の起動はjavaコマンドで行うことができます。ただし、Restletは依存ライブラリの数(アーカイブを展開したlibディレクトリ以下のJARファイルすべて)が多いので、antなどでJARファイルをクラスパスに設定して、コンパイルや起動を行うほうが良いかもしれません。

ここでは1つのRESTアプリケーションのみを設定していますが、もちろん複数のアプリケーションを1つのサーバに設定することは可能です。バージョン1.0のチュートリアルを参考にして挑戦してみてください。

クライアントアプリケーションによるアクセス

サーバを起動できたら、クライアントからアクセスしてみます。もっとも簡単な方法はWebブラウザでアクセスすることです。図1のようにメッセージが表示できれば、ひとまずRESTアプリケーションは完成です。

図1 WebブラウザからRESTアプリケーションにアクセス
図1 WebブラウザからRESTアプリケーションにアクセス

ただRESTアプリケーションの構築は、その性格上、Webブラウザからのアクセスを受け付けることを主な目的とするわけではなく、別のクライアントアプリケーションからのアクセスを受け付けて、マッシュアップ(Mash up)してもらえるようにすることが主な目的のはずです。次はそのためのクライアントアプリケーションについて説明します。

リスト3 RESTアプリケーションにアクセスするクライアント
Client client = new Client( Protocol.HTTP );
Response res = client.get( "http://localhost:8888/" );
System.out.println( res.getEntity().getText() );

リスト3のClientクラスが、RESTアプリケーションにアクセスするクライアントになります。コンストラクタの引数で、サーバへのアクセスに使用するプロトコルを設定します。ここではHTTPのみを設定していますが、複数のプロトコルも利用可能です。

もしRestletを使わない場合は、おそらくJakarta Commons HttpClientなどを用いることになりますが、同じフレームワークのクラスを用いるほうが、複数のフレームワークが混在することを避けられますので、実行環境をシンプルにしておくことができます。これにより、依存ライブラリの管理などのメンテナンスがしやすくなります。これも「軽量」の意味に含まれると言って良いのではないでしょうか。

このクラスのget()メソッドがHTTPのGETに対応し、直観的なプログラミングを可能にしています。引数で与えるURLがサーバの設定と一致しているかどうかを確認しましょう。サーバからのレスポンスとなるメッセージは、メソッドの戻り値であるResponseインスタンスから取得できます。

リスト3のロジックを起動してサーバにアクセスするには、サーバとは別のDOSプロンプトやターミナルのウィンドウを開き、その中でjavaコマンドを実行します。図1と同じメッセージがウィンドウに表示されれば、これでクライアントの構築も完了です。このときサーバ側のウィンドウに例外がスローされるかもしれませんが、接続がクローズされるだけで処理は継続され、次のリクエストを受け付けられる状態になります。

このように、Restletを使うと、同じフレームワークのクラスを用いて、シンプルな方法と環境でRESTアプリケーションを構築することができます。ですが、これではあまりにシンプルで物足りないという読者がいらっしゃいましたら、この後ももう少しお付き合いください。

POSTメソッドの実行

POSTメソッドの実行

これまでは、HTTPのGETメソッドしか実行していませんでした。Web APIがGoogleやYahooなどからリリースされ始めた当初は、彼らが蓄えているデータベースを参照する機能が主に提供されていましたが、最近ではBlogger Data APITwitter APIのように、投稿機能が提供されるようになってきています。

そこで、RESTアプリケーションが投稿を受け付けられるようにするために、ここからはRestletでPOSTメソッドを実行する方法を説明していきます。

リスト4はクライアントアプリケーションの例です。投稿するデータは、生成したFormインスタンスにパラメータとして設定します。同じ名称のパラメータを複数設定することもできます。次に前述したようにClientインスタンスを準備します。ここでもアクセスするURLがサーバの設定と一致していることを確認しましょう。

POSTメソッドの実行は、このクラスのpost()メソッドで行いますが、引数にはFormをWeb上で送受信できる形式(RestletではRepresentationと呼ぶ)に変換したオブジェクトを設定する必要があります。

リスト4 POSTメソッドを実行するクライアントアプリケーションの例
// 投稿するデータ(パラメータ)をフォームに追加
Form form = new Form();
form.add( "a", "パラメータ1" );
form.add( "b", "パラメータ2" );
// クライアントを準備する
Client         client = new Client( Protocol.HTTP );
String         url    = "http://localhost:8888/";
Representation rep    = form.getWebRepresentation();
// 文字コードを指定する場合
// Representation rep = form.getWebRepresentation( new CharacterSet( "Shift_JIS" ) );
// サーバにアクセス
Response res = client.post( url, rep );

リスト5はPOSTメソッドを実行するRestletの例です。クライアントからのリクエストはFormで送信されましたので、RestletではRequest#getEntityAsForm()メソッドを実行して、リクエストからFormを取得します。

この例では、パラメータ名称とデータとが1対1になっていますので、クライアントから送信されたデータはParameter#getFirst()メソッドでそのまま取得できますが、リクエスト中に同じ名称のデータが複数存在する場合は、subList()メソッドを実行するなどして個別にデータを取得するようにします。

リスト5 POSTメソッドによるアクセスを受け付けるRestletの例
Restlet restlet = new Restlet()  {
  @Override
  public void handle( Request request, Response response )  {
    // リクエストからFormを取得する
    Form form = request.getEntityAsForm();
    // パラメータの取得
    Parameter a = form.getFirst( "a" );
    Parameter b = form.getFirst( "b" );
    // 直接データを取得する場合
    // String a1 = form.getFirstValue( "a" );
    // String b1 = form.getFirstValue( "b" );
    // 複数のデータを個別に取得する場合
    // String a2 = form.subList( "a" ).get(1).getValue();
    // String b2 = form.subList( "b" ).get(1).getValue();
    // .......... その他の処理 .....
  }
};

BASIC認証を行う

データベースの参照は、アクセス負荷を考慮する必要がある程度で、あとはそれほど大きな問題はないでしょうが、投稿や更新を行う場合は、アクセスするユーザをある程度制限するのは、セキュリティ上あるいは投稿の内容の問題などがあり、どうしても必要になります。そのときWeb APIではBASIC認証でアクセスを制限することが一般的になっています。

WebブラウザでBASIC認証が必要なWebサイトにアクセスすると、閲覧の直前にユーザ名とパスワードの入力が求められます。しかし、クライアントアプリケーションの場合は人間のユーザがいちいちこれらの入力をすることができませんので、クライアントアプリケーションにあらかじめユーザ名とパスワードを設定してサーバにアクセスします。

リスト6がその例です。BASIC認証に関係するのはChallengeSchemeとChallengeResponseです。サーバにアクセスする前にこれらをRequestに設定した上でアクセスします。認証されたかどうかはStatusで判断します。

リスト6 BASIC認証を要求するサーバにアクセスするクライアントアプリケーションの例
// BASIC認証用のリクエストを設定(ユーザ名とパスワード)
ChallengeResponse basicAuth = new ChallengeResponse(
    ChallengeScheme.HTTP_BASIC, "username", "password" );
Request request = new Request( Method.GET, "http://localhost:8888/" );
request.setChallengeResponse( basicAuth );

// サーバにアクセス
Client client = new Client( Protocol.HTTP );
Response response = client.handle( request );

// アクセスできたかどうかを確認
Status status = response.getStatus();
if ( status.isSuccess() ) {
  // ..... 認証された場合の処理 .....
} else if ( status.equals( Status.CLIENT_ERROR_UNAUTHORIZED ) ) {
  // ..... 認証されなかった場合の処理(Status.CLIENT_ERROR_FORBIDDENが返される場合もある) .....
} else {
  // ..... その他のエラーでアクセスできない場合の処理 .....
}

一方、RestletにもBASSIC認証を設定できます。この場合はリスト7のように、サーバの設定にComponentインスタンスを使用します。これはBASIC認証の設定を行うGuardインスタンスに実行環境の状態を設定する必要があるためです。Webブラウザからこれにアクセスすると、図2のように認証用のダイアログが表示されます。

リスト7 BASIC認証を必要とするRestletの例
// RESTアプリケーション
Restlet restlet = new Restlet()  {
  @Override
  public void handle( Request request, Response response )  {
    // ..... RESTアプリケーションの処理 .....
  }
};

// サーバの設定
Component component = new Component();
component.getServers().add( Protocol.HTTP, 8888 );
// BASIC認証の設定
Guard guard = new Guard( component.getContext(), ChallengeScheme.HTTP_BASIC, "An Interest Products" );

// ユーザ名とパスワード
guard.getSecrets().put( "username", "password".toCharArray() );
guard.setNext( restlet );

// サーバにアプリケーションを設定
component.getDefaultHost().attach( "", guard );

// サーバ起動
component.start();

図2 認証用の入力ダイアログが表示された
図2 認証用の入力ダイアログが表示された

XMLデータの取得

Web APIの実行結果はXMLデータとなる場合が多いので、それをアプリケーション内で処理しやすいオブジェクトで取得できると便利です。そこで、最後にレスポンスのXMLデータをDOMオブジェクトで取得するクライアントアプリケーションを紹介します。

リスト8は、いまこの記事をお読みいただいているgihyo.jpのRSSデータを取得して、記事タイトルを出力する例です。ここではサーバへのアクセス時にClient#getEntityAsDom()メソッドを実行して、レスポンスをDomRepresentationインスタンスで取得しています。

こうすると、getNodes()メソッドで必要なデータをXPath式で取得できます。また、getDocument()メソッドでDocumentインスタンスを取得すれば、XSLTによってデータを変換することもできますし、レスポンスのすべてのデータにいつでもアクセスできます。

リスト8 gihyo.jpのRSSデータを取得し、記事のタイトルを出力
Client client = new Client( Protocol.HTTP );
DomRepresentation rss = client.get( "http://www.pheedo.jp/f/gihyo_rss2" ).getEntityAsDom();
for( Node n : rss.getNodes( "//item/title" ) )  {
  System.out.println( "タイトル = " + n.getTextContent() );
}

図3 出力結果(例)
タイトル = 2007年6月8日 《注目》Metisse追加、開発版Video LAN更新、Wackamole/Abraca/Lens追加、PHP5更新、ほか
タイトル = 第2回 Prologをご存じですか?
タイトル = 第2回 対話を促進する学び場の運営ノウハウ[中編] 勉強会当日の進め方

.......... ほかにもいろいろなタイトルが出力される .....

Restletは、RESTアプリケーションをシンプルに構築するための手段を用意しています。Web APIの実行結果がJSONだったらどうすればよいでしょうか。その場合は拡張ライブラリのJsonRepresentationクラスを用いることができます。他にもJDBCやSpring Frameworkと連携するクラスもあります。

紹介しきれないクラスはまだいろいろありますので、Web APIに興味がある読者は、Restletを試してみてください。

おすすめ記事

記事・ニュース一覧