今どきのウェッブサービスの作り方
みなさんRESTfulなWEBサービス作るときなにを使いますか?
・・・PHP?ああ、はい、なるほどね。
Ruby?なんですかそれは?きいたことがないですね。
Java?それは素晴らしい選択です!
ああ、でもJavaEEはダメです。アレは古い上に複雑すぎて。
今どきのハイカラな技術者はみなさんJavaSEを使っていますよ。ええ、もちろんとっておきの簡単フレームワークがあるんです。
まあとりあえず落ち着いて、これをダウンロードしてみて下さい。
Downloadsというところから、バージョンは・・・そうですね、testingで結構です、JavaSEバージョン、ZIP版を選びましょう。
あっ、Mavenをお使いですね?それでしたら簡単です。
専用リポジトリを追加して
<repositories> <repository> <id>maven-restlet</id> <name>Public online Restlet repository</name> <url>http://maven.restlet.org/</url> </repository> </repositories>
依存を追加するだけです
<dependency> <groupId>org.restlet.jse</groupId> <artifactId>org.restlet</artifactId> <version>2.1.0</version> </dependency>
さあ、準備は整いました。早速コードを書きましょう。今回のサンプルは/testというアプリケーション内に/helloというリソースがあるというサービスです。
public static void main(String[] args) throws Exception { Component component = new Component(); component.getServers().add(Protocol.HTTP, 8888); component.getDefaultHost().attach("/test", new TestApplication()); component.start(); }
RestletはRESTfulな概念に忠実に倣ったフレームワークです。ComponentにApplicationを繋げて行きましょう。TestApplicationは今回のサンプルアプリケーション「/test」です。
public class TestApplication extends Application { @Override public synchronized Restlet createInboundRoot() { Router router = new Router(getContext()); router.attach("/hello", HelloServerResource.class); return router; } }
Routerは文字通りリソースへのルーティングを行います。ここで複数のリソースを登録できます。今回は/helloというリソースを登録しましょう。はい、そしてそれはインスタンスではなくクラスです。
public class HelloServerResource extends ServerResource { @Get public Representation getMessage() throws JSONException { JSONObject result = new JSONObject(); result.put("message", "Hello Restlet!"); return new JsonRepresentation(result); } }
おっと、依存を追加するのを忘れました。
<dependency> <groupId>org.restlet.jse</groupId> <artifactId>org.restlet.ext.json</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20090211</version> </dependency>
Restletは多彩な拡張機能を提供します。例えば今追加したのはJSON拡張。レスポンスをJSONで返したり、JSONのリクエストを処理したりするためです。だって今どきJSONでしょ?
さあ、これで立派なウェッブサービスが完成しました!起動してみて下さい。・・・どうでしたか?RESTfulな世界を体験できましたか?とっても簡単でしょう?
ここからはRestletをフル活用するためのヒントを幾つか紹介しましょう。
1. 統一されたエラーレスポンス
通常Exception発生時には500コードと共にInternal Server ErrorのHTMLページが出てきてしまいます。サービス的には適切なオブジェクトを返すのが無難でしょうから、ServerResource#doCatchをオーバーライドしましょう。
@Override protected void doCatch(Throwable t) { final ZythumException zex = ZythumException.toZythumException(t); if (zex.getCode() == ZythumException.CODE_GENERAL_ERROR) { t.printStackTrace(); } getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST); getResponse().setEntity(responseErrorRepresentation(zex)); } public static Representation responseErrorRepresentation(ZythumException ex) { try { return new JsonRepresentation(ex.createErrorDocument()); } catch (JSONException e) { StringRepresentation r = new StringRepresentation( "{\"error\":3,\"message:\":\"JSON Error\"}"); r.setMediaType(MediaType.APPLICATION_JSON); return r; } }
これらのコードはあるサービスの一部ですが、次のような手製Exceptionを作成しておくと便利です。
public static ZythumException toZythumException(Throwable t) { if (t instanceof ZythumException) { return (ZythumException) t; } else if (t.getCause() instanceof ZythumException) { return (ZythumException) t.getCause(); } else if (t instanceof JSONException) { return new ZythumException(CODE_MALFORMED, "JSON Error", t); } else { return new ZythumException(CODE_GENERAL_ERROR, "General Error", t); } }
2. MongoDB
MongoDBは本当に素晴らしいDBです。私はRestlet用のDBには必ずこれを使います。
Mongoインスタンスはシングルトンにしておくと良いでしょう。
3. OAuth2.0拡張
今どきのウェッブに必須なOAuth2.0用拡張も用意されています。現在のバージョンはdraft10ですが、次期バージョン(2.2)ではdraft31に対応する予定です!
4. Tanuki Java Service Wrapper
Restletでは公式にTanuki Software社のWrapperを利用することを推奨しています。
最も簡単な手段はWrapperSimpleAppを使用することです。
5. クライアント
Restletはサーバーサイドだけでなく、クライアントサイドのフレームワークとしても長けています。例えば先程のサンプルサービスのクライアントサイドを書こうと思ったら
ClientResource resource = new ClientResource("http://localhost:8888/test/hello"); Representation representation = resource.get(); JSONObject result = new JsonRepresentation(representation).getJsonObject();
簡単でしょう?しかもサーバーサイドとアーキテクチャが同じことに注目して下さい。サーバーサイドがRepresentationとしてレスポンスしているのが、そのままクライアントサイドでもRepresentationとして受け取ります。
実はサーバーサイドのスタブのようなものを作成して、まるで直接相手のServerResourceを呼び出しているかのような記述も可能なんですよ。
6. Connector
サーバーサイド、クライアントサイド共に、Connectorを指定することができます。実際のサーバ、クライアントの実装を差し替えることができるのです。
例えばサーバーサイドだとデフォルト実装、Jetty、JDKNet等。私のおすすめは断然Jettyです。差し替えはJetty拡張とJetty本体をクラスパスに含めるだけ、お手軽です!
7. XML Configuration
Componentのセットアップをベタにmain内に書くのももちろんOKですが、サービスが複雑になるとメンテナンスが大変になります。XML設定ファイルを用いれば、ComponentからVirtualHostの設定まで、柔軟に記述できます。(XMLファイルとして外部URLも指定できます。)
いかがでしたか?PHPやJavaEEは今すぐ忘れて、貴方もRestletで素敵なRESTfulウェッブサービスを作りましょう!
この記事はJava Advent Calendar 2012の21日目のエントリでした。
前回は@kisさんのJavaでのパターンマッチを考えるでした。
次回は@masudaKさんです。