Monday, December 23, 2013

JAX-RSのビューとしてMixer2を使ってみる

こんにちは。nabedgeこと渡辺です。 ひとつ前の記事はJava Advent Calendar2013のやつでしたが、 今日はJava EE Advent Calendar 2013の23日目なんです。 JavaじゃなくてJavaEEです。ここでSpringMVCがどうのとか言っちゃうといろんな人が(コンコン、おや誰かきたようだ

そんなわけで、JavaEE規格の中でもWeb系エンジニアにとって最も身近な、JAX-RSのお話です。

今年の3月頃、 JAX-RSはHTML Webアプリケーションを開発するのに充分なフレームワークであるか? - AOEの日記 というエントリが結構話題になりまして、そこではこんな話もありました。

テンプレート処理パートの実装について

(中略)
ただ、普通のJSPプログラミングであるため、エラー時にメッセージやハイライトをしたり、条件に応じた表示変更と言った処理もゴリゴリ実装する必要があります。これが地味にめんどかったりします。少し大きな規模の開発案件になった場合はある程度プレゼンテーションレイヤで必要となるライブラリを整備してあげる必要があると思います。

そのとおりです。javaのmvcフレームワークは、案外どれも「ビューはjsp(jsf)で」と一言書いてあるだけです。 しかし現実世界に置けるM,V,CのViewは、それほど単純ではありません。むしろ、ビューは十分に複雑です。

JAX-RSの設計思想として、httpレスポンスをhtmlではなくjsonやxmlで返すことに主眼を置いているように思えます。 素直に考えると、クライアントサイドはJavaScript等で実装しサーバーから受け取ったjsonやxmlを料理せよ、ということでしょうし、 実際そうしようとしている開発現場も増えてきているかもしれません。 しかしそのためにはBackbone.jsやAngular.jsといったクライアントサイドMVCフレームワークの使い方も覚えなければなりません。 Webデザイナーの仕事とうまく歩調を合わせることまで考えると、道のりは険しそうです。

話をサーバーサイドに戻しましょう。 Viewを実装するために、jsp、jsfあるいはVelocityやThymeleafのようなテンプレートエンジンの特殊文法を覚えたり、 それでも足りなくてカスタムタグやプラグインを書き、気がつけばWebデザイナーさんの仕事は置いてけぼりになっている。 そんなことになるくらいなら、はじめからViewをjavaで普通に書けばいいじゃない! テンプレートは普通のhtml/cssで!それがMixer2の存在理由です。

前置きが長くなりましたがとにかく、html主体のwebアプリケーションを、JAX-RSとMixer2で作る、簡単なサンプルを作ってみました。 ソースは https://github.com/nabedge/mixer2-sample/tree/master/mixer2-jaxrs-sample です。

jax-rs実装にはjersey、サーバはtomcatを想定しています。(あっ、なんでglassfishじゃねーんだって言われそう^^;)

ポイントは二つ。まず一つ目が、リソースクラスのメソッドの戻り値がorg.mixer2.jaxb.xhtml.Html型であることです。 IntexResource.javaをご覧ください。

    @GET
    @Produces(MediaType.TEXT_HTML)
    public Html index(@QueryParam(value = "message") String message)
            throws IOException, TagTypeUnmatchException {

        // loading template
        String template = "m2mockup/m2template/index.html";
        InputStream is = this.getClass().getClassLoader()
                .getResourceAsStream(template);
        Html html = mixer2Engine.loadHtmlTemplate(is);
        
        // replace content in <div id="message"> tag
        if (message != null) {
            Div div = html.getBody().getById("message", Div.class);
            div.unsetContent();
            div.getContent().add(message);
        }
        
        // replace static file path
        Pattern pattern = Pattern.compile("^\\.+/.*m2static/(.*)$");
        PathAjuster.replacePath(html, pattern, "m2static/$1");
        
        // return html object
        return html;
    }

Html型を返されたjerseyは、それを処理しうるMessageBodyWriterを探して引き継いでくれます。 それがMixer2Writer.java です。

@Provider
@Produces(MediaType.TEXT_HTML)
public class Mixer2Writer implements MessageBodyWriter {

    // -- snip --

    @Override
    public void writeTo(Html html, Class type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap multivaluedMap,
            OutputStream outputStream) throws IOException, WebApplicationException {
        // marshalling html object to string.
        String str = mixer2Engine.saveToString(html);
        OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
        writer.write(str);
        writer.flush();
    }
}

Html型のインスタンスをMixer2Engineに渡して文字列化し、そのまま出力しているだけです。

いろいろ急いでしまったので、突っ込みどころだらけです。リソースクラス内でビューの処理もやってしまっている点はイケてなくて、 本来ならViewableViewableContext をうまく実装して切り分けるべきでしょう。そもそもJavaEEだっつってんのになんでGlassFishじゃなくtomcatで試しているんだとか。 Mixer2Engineの呼び出しもCDIを使うほうがJavaEEらしくなるでしょう。

このサンプル、どなたか試していただいて、もちょっとカッコよくしていただけませんか? マサカリも歓迎ですが、Pull Requestはもっと歓迎です。だってほら、クリスマスだし。

No comments:

Post a Comment