Java Enthusiast

3 Billion Devices Run Java

今どきのウェッブサービスの作り方

みなさんRESTfulなWEBサービス作るときなにを使いますか?

・・・PHP?ああ、はい、なるほどね。

Ruby?なんですかそれは?きいたことがないですね。

Java?それは素晴らしい選択です!

ああ、でもJavaEEはダメです。アレは古い上に複雑すぎて。

 

今どきのハイカラな技術者はみなさんJavaSEを使っていますよ。ええ、もちろんとっておきの簡単フレームワークがあるんです。

 

まあとりあえず落ち着いて、これをダウンロードしてみて下さい。

http://www.restlet.org/

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も指定できます。)

 

いかがでしたか?PHPJavaEEは今すぐ忘れて、貴方もRestletで素敵なRESTfulウェッブサービスを作りましょう!

 

 

この記事はJava Advent Calendar 2012の21日目のエントリでした。

前回は@kisさんのJavaでのパターンマッチを考えるでした。

次回は@masudaKさんです。