JAX-RS + mustache
これは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フレームワークってどうなってるの?
画面の初期表示
値を入力すると足し算を行います。
テンプレートはこんな感じ。
{{ と }} で囲みます。ホントに口髭みたいですね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