これはJava EE Advent Calendar 2013の2日目のブログエントリーです。

昨日は@kikutaro_さんのJSF2.2のFaces Flows(FlowScoped)でした。
明日は@backpaper0さんです。よろしくおねがいします!

   

9月に開催されたJavaOne2013でJersey 2 MVC in Actionというセッションがあったそうです。

Jersey 2 MVC in Action [BOF5548]

JAX-RSの参照実装であるJerseyの独自機能ではありますが、良い機能が色々と含まれていて、テンプレートエンジンにmustacheも使えます。

個人的にはJerseyMVC自体をJavaEEの標準機能にして欲しいと思っているのですが、
JSFがあるので、それ以外のMVCフレームワークが標準機能に含まれる事は無いのかなぁ。。。

   

現時点では、まだ新規プロジェクトでJavaEE6を採用するケースが多いんじゃないかと思いますが、
JavaEE6(GlassFish3.1.2.2)でJAX-RSのテンプレートエンジンにmustacheを使えるようにするjersey-mustacheというのがあったので試してみました。
jersey-mustacheはMVCフレームワークではなく、mustacheをテンプレートエンジンに使えるようにするためのViewProcessorです。

 

今回はSonatypeにあるSnapshotを使うのでrepositoryを追加します。

<repositories>
    <repository>
        <id>sonatype-nexus-snapshots</id>
        <name>Sonatype Nexus Snapshots</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
</repositories>

 

次にdependencyを追加。
jersey-mustacheがGlassFish3.1.2.2に含まれているバージョンと違うjersey-serverとjersey-coreに依存しているため、exclusionsで除外しました。

<dependency>
    <groupId>org.eluder.jersey</groupId>
    <artifactId>jersey-mustache</artifactId>
    <version>1.1.0-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

 

次にGlassFish3.1.2.2に含まれているJerseyのdependencyをprovidedで追加。

<dependency>
    <groupId>com.sun.jersey.glassfish.v3.osgi</groupId>
    <artifactId>jersey-gf-server</artifactId>
    <version>1.11.1</version>
    <scope>provided</scope>
</dependency>

 

そして、JAX-RSを有効にします。
javax.ws.rs.ApplicationPathを継承したクラスを作れば良いのですが、今回はweb.xmlにしました。
ついでにjersey-mustacheの初期パラメータを指定しています。

  • mustache.resource.root -> テンプレート配置ディレクトリのルート
  • mustache.template.expiry -> テンプレートキャッシュの有効期限(単位: ミリ秒)
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <servlet>
        <servlet-name>Jersey REST Service</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>info.matsumana.resource; org.eluder.jersey.mustache</param-value>
        </init-param>
        <init-param>
            <param-name>mustache.resource.root</param-name>
            <param-value>/templates</param-value>
        </init-param>
        <init-param>
            <param-name>mustache.template.expiry</param-name>
            <param-value>3600000</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey REST Service</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

 

サンプルとして、こんな画面を作りました。
参考ページ: 今どきのJava Webフレームワークってどうなってるの?

 

画面の初期表示

初期表示

 

値を入力すると足し算を行います。

submit後

 

テンプレートはこんな感じ。
{{ と }} で囲みます。ホントに口髭みたいですねw
Mapの場合はキーで参照、POJOの場合はフィールド名で参照します。POJOのフィールドはpublicでもOKです。

    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>mustache Page</title>
    </head>
    <body>
        <form action="calc" method="POST">
            <h1>Hello JAX-RS!</h1>
    
            <input name="left" value="{{left}}" />
            <input name="right" value="{{right}}" />
            <br/>
            <input type="submit" value="足す" />
            答え {{ans}}
        </form>
    </body>
    </html>

 

リソースクラスはこんな感じ。
mustacheとは関係ありませんが、POSTされたフォームの値をCalcResourceParamというDtoにマッピングして受け取れるようにしています。
参考ページ: JAX-RS (Jersey) でパラメータとして独自のクラスをインジェクトする (1)

@Named
@RequestScoped
@Path("calc")
@Produces(MediaType.TEXT_HTML)
public class CalcResource {

    @Inject
    CalcDomain calcDomain;

    @GET
    public Viewable index() {
        return JaxrsUtil.newViewable("/calc.mustache");
    }

    @POST
    public Viewable calc(@Context ResourceContext rc) {
        CalcResourceParam param = rc.getResource(CalcResourceParam.class);
        calcDomain.calc(param);

        return JaxrsUtil.newViewable("/calc.mustache", param);
    }
}

 

これがフォームの入力値をマッピングさせるDtoです。
フィールドに@FormParamアノテーションを付けます。
publicフィールドが嫌いな人はLombokをどうぞ。

public class CalcResourceParam {

    @FormParam("left")
    public Integer left;
    @FormParam("right")
    public Integer right;
    public Integer ans;
}

 

ドメインは計算しているだけ。

@Named
@RequestScoped
public class CalcDomain {

    public void calc(CalcResourceParam param) {
        param.ans = param.left + param.right;
    }
}

 

JavaにはFreeMarkerという定番テンプレートエンジンがあるので、mustacheはあんまり使われてないと思いますが、
Jersey 2 MVCをきっかけにシンプルさがウケけて流行るかも知れませんね!

ソースはこちらに置いています。
jersey-mustache-sandbox