Saturday, December 28, 2013

Mixer2の最近のリリースでのエンハンス(特にSpringMVC関連)

Mixer2-1.2.22をリリースしました。

実はここ数週間で何回かリリースしましたが、まともにアナウンスしていませんでした。最近のリリースではどこをどういじったか、ざっくりまとめておきます。

1. Spring MVCとの組み合わせ方をドキュメントに書きました

Mixer2とSpringMVCと組み合わせたサンプルアプリケーションはもともとあったのですが、 javadocはともかく、文章の形では基本的な説明をまとめて書いてはいなかったので、 Use Mixer2 with Spring MVCというページを公式サイトにつくりました。 なお、もともとほぼ同じことがjavadocにも書いてありましたが、それも少し手直ししました。

※誤解の無いように念のため書いておきますが、Mixer2それ自体は他のライブラリ等への依存性が非常に少ない、独立性の高いxhtmlテンプレートエンジンです。 Webアプリケーションフレームワークと組み合わせて使うのが便利なことは確かですが、 必ずSpringMVCやSAStrutsと組み合わせないと何もできないということは全くありません。

2. Spring MVCでMixer2もJSPも両方使いたくてもうまく動かないのを直しました

公式サイトの説明のように


    
        
        
        
        
        
    
    
        
        
        
    

と書けば、コントローラから返されたview名を使ってMixer2ViewResolverがhtmlテンプレートファイルを見つけられなかった場合には、 そのままInternalResourceViewResolverへ処理が移って、view名に該当するjspが使われる...はずだったのですが、 実際にはMixer2VeiwResolverがhtmlテンプレートを見つけられなかった段階でいきなりFileNotFoundExceptionになってしまっていました。

それではまずいので、ちゃんと次のリゾルバーに処理が移るように直しました。このへんは、SpringMVCのマニュアルにも書かれている通りにしてみた、という感じです。

17.5.2 Chaining ViewResolvers

"The contract of a view resolver specifies that a view resolver can return null to indicate the view could not be found. Not all view resolvers do this ..." (ビューリゾルバーの実装ルールとしては、ビューを見つけられない場合にはnullを返すことができますよ。ただ全部のビューリゾルバーがそうしてるわけじゃなくケースバイケース...)
Spring MVC reference 3.2.xより

ただし、従来の挙動(見つからなければ即座にFileNotFoundException)でよいという人のために、 この機能をオフにする設定方法も用意しておきました。細かいことはMixer2XhtmlViewResolverのjavadocに書いてあります。

3. headタグの中ではremoveById()等が動かないことがあるのを直しました

単純にバグです。

4. 恥ずかしいミススペルを直しました

PathAjusterクラスはクラス名自体の綴りが間違っていることが今頃発覚w。 機能はそのままでPathAdjusterクラスとして作り直しました。 古い方のクラスは@Deprecatedにしておきましたが、こういうので後方互換性を失うのはまずいのでたぶんずっと消さずに残しておくと思います。

5. HTML5でAタグの中のDIVタグ等が消えるのを直しました

Mixer2-jaメーリングリストのほうでもらったバグ報告の対応です。

htmlテンプレートとしてmixer2Engineがロードするとき、テンプレート上のaタグの中にインライン要素(spanとかimgとか)が入ってるのはokだが、 ブロック要素(divとかpとか)が入っているとそれが消えてしまうという現象です。

これはちょっと盲点でした。というのは、html4.xまでの規格では、aタグの中にブロック要素を入れるのはNGなのです。 Mixer2はJAXBを使ってhtml(xhtml)を読み込むので、そういったところも厳格にNG扱いで、該当要素を無かったことにします。 ところが、html5ではaタグの中にブロック要素も入れられるようになりました。 HTML5 の “ブロック・レベル“ リンクというやつです。 Mixer2のhtml5に対する対応の不足、というバグとして対応しました。

ここ数週間のMixer2のエンハンス、ざっくり解説してみました。安心して最新版をご利用ください。今後ともごひいきに!

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はもっと歓迎です。だってほら、クリスマスだし。

Sunday, December 8, 2013

Mixer2のSpringMVC連携機能がver 1.2.17でさらに進化!

こんばんは。nabedgeこと渡辺です。 この記事はJava Advent Calendar 2013の8日目です。

さて、私が作っているMixer2というテンプレートエンジンですが、 秋口に秀逸な pull request をいただいたので、ありがたくマージしてversion1.2.17をリリースしました。

本当は先週リリースした1.2.14で既にマージ済みだったのですが、 JavaDocに書いたサンプルコードのとおりに書くと動かない(笑)というヤバいのがあったのでリリースしなおしました。 そのJavaDocを書いたのはプルリクをくれた人ではなく私です。 さらに言うと、リリース作業で凡ミスして1.2.15と1.2.16を欠番にしてしまったのも私です。 ^^;)

そんなことはさておき、何が新しくなったのかというとSpringMVCとMixer2を連携させるためのViewとViewResolverです。 とりあえずフルーツショップサンプルアプリケーションの商品配送先住所の入力画面用のコントローラで説明してみます。

@RequestMapping(value = "/shipping")
public String shipping(Model model,
    @RequestParam(value = "redirected", required = false) boolean redirected,
    @Valid Shipping shipping, Errors errors) {

    model.addAttribute("shipping", shipping);
    model.addAttribute("redirected", redirected);
    model.addAttribute("errors", errors);
    return "checkout/shipping";
    // 以前のMixer2ではここで return "shippingView" のように、
    // springコンテナ上にDI済みのviewクラスの名称を
    // 与える必要があったが、新しいMixer2では
    // テンプレートファイルのパスでよい。
    // そしてテンプレートファイルのパスは
    // Viewクラスのパッケージ名/クラス名とそのまま紐づくルール。
}

コントローラの戻り値はテンプレートファイルのパスそのものです。 つまり戻り値の書き方はInternalResourceViewResolver + JSPを使う場合と同じでいいところがポイントです。

次に、配送先住所の入力画面のViewクラスを見てみましょう。

// Viewクラスに@ComponentのようなDIアノテーションは不要。
// 新しいViewResolverが代わりにやってくれる。
public class ItemView extends AbstractMixer2XhtmlView {

    @Autowired
    protected FooBar fooBar; 
    // もちろんSpringコンテナから任意の
    // オブジェクトをDIで受け取れる

    @Override
    protected Html renderHtml(Html html, 
            Map<String, Object> model, 
            HttpServletRequest request,
            HttpServletResponse response) 
            throws TagTypeUnmatchException {

        // Html html = mixer2Engine.loadHtmlTemplate(filePath);
        // のようなコードは不要。
        // 新しくなったAbstractMixer2XhtmlViewが、
        // コントローラのメソッドの戻り値で指定された
        // パスを使ってあらかじめテンプレートをロードして
        // Htmlオブジェクトに格納済み。

        @SuppressWarnings("unchecked")
        List<Category> categoryList = (List<Category>) model.get("categoryList");
        Item item = (Item) model.get("item");

        // embed item box
        replaceItemBox(html, item);

        // 途中は省略

        return html;
    }

ここでSpringの設定のほうを見てみます。



    
    
    
    
    

Mixer2XhtmlViewResolverのprefix,suffix,basePackageのプロパティがポイントです。 つまり、コントローラのメソッドの戻り値が "foo" の場合、
org.mixer2.sample.web.view.FooView がビュークラスで、
クラスパス上の m2mockup/m2template/foo.html がテンプレートファイルです。

コントローラのメソッドの戻り値が "foo/bar" の場合、
org.mixer2.sample.web.view.foo.BarView がビュークラスで、
m2mockup/m2template/foo/bar.html がテンプレートファイルということになります。

もちろんビュークラスはAbstractMixer2XhtmlViewを継承していなければなりません。 なお、複数のテンプレートを組み合わせてhtmlを生成したい場合は、ビュークラス内で getMixer2Engine()でいつでもエンジンを使えますので、好きなテンプレートをいくらでもロード可能です。

クレバーなプルリクをくださったkazuki43zooさんに感謝!

Java Advent Calendar 2013、 次はJavaFXの大御所、skrbさんです。

Wednesday, November 27, 2013

メールアドレスとRFCの話

確かに定期的に繰り返される話題ですね。

「メールアドレスのルール」なんて使ってはいけない3つの理由

問題点1. メールアドレスに関して、RFCなんてそもそも守られていない

2009年以前に登録されたDoCoMo携帯のメールアドレスなど、quoted-stringじゃないのにピリオド連続するものが実在している以上、彼らを許容するべきです。

今そこにある実装 >> (越えられない壁) >> RFC です。

※2009年以前に登録されたDoCoMo携帯のメールアドレスっていうのはつまり、 foo....bar@docomo.ne.jp みたいにドットが連続してたりするアドレス(RFC的にはNG)のことを指します。

んで、「今そこにある実装」を重視すべきなのであれば、以下も重視したうえで、 「やはり彼らを許容すべきではない」という結論も得られてしまいます。

ケースバイケースでしょう。

合わせて読みたい:Internet Explorer はIE特化アプリから一時的に日本を救ってくれたけど次回はもう助けてくれないよという 警告

Sunday, November 24, 2013

DevLove現場甲子園でしゃべってきました

もう先々週のことなんですが、DevLOVE現場甲子園2013というイベントでしゃべってきました。

webデザイナとエンジニアのチームワークを加速させるテンプレートエンジンmixer2 devlove現場甲子園

Mixer2ネタであることはともかく、プレゼン資料自体の使い回し感は否めません。すいませんすいませんすいません。

ほかの方もプレゼンもなかなか面白かったです。 webデザイナーの秋葉さんのプレゼンとか→ 「【DevLOVE甲子園2013】UIと画面遷移を設計するときに 破綻しないようにするための、 ひと手間

あと、「バラバラの同僚を社内勉強会でつなげよう」っていうやつもなかなか。

最後になりましたが、devloveスタッフの方、本当にご苦労様でした。ありがとうございました。

Saturday, October 26, 2013

log4j2にログを集める

log4j, slf4j, logback, commons-logging... こんがらがりませんか?僕はこんがらがりました。

ある実験的なWebアプリの開発の過程では、ログを最終的に何に集約するか?logbackか?それとも...?と、迷い、 結局log4j2に集約しました。これはそのメモみたいなものです。

なお、「slf4j+logbackのほうがナウでヤングだろ」といったご意見はスルーです。好みとかポリシーの問題です。 また、いにしえより伝わるlog4j1.xではなく、log4j2.xっていうところもポイントです。 java.util.logging?えーっ?!却下。

1. 前提とか方針とか

  • 新規のJava-Webアプリを開発する。そのコード上で使うLoggerにはlog4j2を使う。
  • ベースはSpringMVCフレームワーク。spring自身は内部でcommons-loggingを使っているので、それもlog4j2に集められるように設定する。
  • Spring以外にもいろいろOSSライブラリを使うが、それが吐くログもできる限りlog4j2に集める。例えば、内部でslf4j-apiを使ってるOSSが最近はとても多い。
  • 設定はlog4j2.xmlに集める。とりあえずWebアプリ内部に同梱する。
  • ビルドツールはmavenを使う

2. mavenのpom.xml

dependenciesタグ内で先に書いてあるほうが優先されるので、ログ出力ライブラリは上のほうに書いておくのが無難です。log4j-coreがもちろんlog4j2の本体。 log4j-jclがcommons-loggingによる出力をlog4j2に中継させるやつ。log4j-slf4j-implはslf4jによる出力を(以下略)

    
        
            org.apache.logging.log4j
            log4j-core
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-jcl
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-slf4j-impl
            2.0-beta8
        

        ... other dependency...
    

maven使いじゃない人のために、これで実際どんなjarがビルドパスに入ってくれるのか、書いておきます。こんだけ。

log4j-core-2.0-beta8.jar
log4j-api-2.0-beta8.jar
log4j-jcl-2.0-beta8.jar
commons-logging-1.1.1.jar
log4j-slf4j-impl-2.0-beta8.jar
slf4j-api-1.7.5.jar
slf4j-ext-1.7.5.jar
cal10n-api-0.7.4.jar

3. log4j2.xml

log4j2.xmlをsrc/main/resourcesに置く。root(要するにデフォルト値)を設定するほか、 ログ出力を制御したいクラス名、パッケージ名に対して出力レベルや出力先(appender)を設定します。

4. コードを書く

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Foo {
    private static Logger logger = LogManager.getLogger();
    public void bar(String a, String b) {
        logger.debug("a={},b={}", a, b);
    }
}

ログに変数をいれたいときに、logger.debug("a=" + a + ",b=" + b ) などとやってしまいがちですが、 実はこれ、ログ出力メソッドよりも先に+演算子による文字列連結の方が先に実行されますので、性能影響が出ます。 logger.debug()なんて本番環境ではオフにすると思いますが、それでも文字列連結は無駄に実行されてしまうのです。 どの程度の性能差が出るかは Performance - Apache Log4j 2 に詳しく解説されています。 せっかくlog4j2を使うのですから、{}記号によるプレースホルダ機能を使うようにしましょう。

また、log4j1.xのときは、
provate static Logger logger = Logger.getLogger(Foo.class);
のように、ロガー名として自クラスを明示的に与えてloggerを作る必要がありましたが、 log4j2のLogManagerでは省略できます。 log4j2のほうが好みな理由はこれが地味に便利だからかも。

Tuesday, October 1, 2013

Mixer2 as XHTML snippet generator

Mixer2 is the xhtml dedicated template engine. Because Mixer2 use JAXB (Java Architecture for XML Binding) internally, the usage is not only as a template engine, but also, Mixer2 can be used in another way.

Mixer2 has some different functionality.

  1. as a alternative to JSP, Velocity, and other template engine technology
  2. as a XHTML parser
  3. as as XHTML tag snippet generator

In thie post, I introduce "XHTML snippet generator" functionality of Mixer2.

You must fix up your html template as "full html" like "<html>...</html>" in order to use mixer2Engine.loadHtmlTemplate() .

But if you need to output xhtml tag using mixer2Engine.saveToString(), you can get any tag snippet as string. So, you can create some helper class like custom tag of JSP.

For example, If you must use jsp as the base template but also you want to use Mixer2, You can use them combine them.

If you create the new tag instance, use "new" the tag classes. Also you can use TagCreator.

Div div = TagCreator.div();
Span span = TagCreator.span();
span.getContent().add("Hello World !");
span.setStyle("color:red;");
div.getContent().add(span);
String result = mixer2Engine.saveToString(div);
System.out.println(result);

output:

Hello World !

If you create table tag, you can use TableBuilder.

String[][] data = new String[][]{
    // id, name, price
    {"1", "cookie", "2.50"},
    {"2", "candy",  "3.75"}
};

TableBuilder tBuilder = new TableBuilder();
for (String[] row : data) {
    tBuilder
        .addTr()
        .addTd(row[0])
        .addTd(row[1])
        .addTd(row[2]);
}
Table table = tBuilder.build();
String str = mixer2Engine.saveToString(table);
System.out.println(str);

output:

1 cookie 2.50
2 candy 3.75

For more detail of TableBuilder, see Mixer2 official site.

How about it ? Mixer2 and other template technology can exist together. Try Mixer2 !

XHTMLタグスニペット生成器としてのMixer2

Mixer2はXHTML専用のテンプレートエンジンです。 しかし、Mixer2が内部でJAXB(Java Architecture for XML Binding)を使用しているため、 単なるテンプレートエンジンにとどまらない使い方が可能です。
大きく分けて、Mixer2には次のような側面があります。
  1. JSPやVelocityに代わる技術としてのMixer2
  2. XHTMLパーサーとしてのMixer2
  3. XHTMLタグスニペット生成器としてのMixer2
この記事では、3番目の、XHTMLタグスニペット生成器としてのMixer2の使い方を紹介します。
Mixer2は、XHTMLをパース - loadHtmlTemplate() - する場合には フルHTML、つまり<html>...</html>までのHTML全体を与える必要があります。
しかしXHTMLタグを出力 - saveToString() - する場合には 任意のタグのみをString化することが可能です。 この特徴をうまく使えば、JSPのカスタムタグのようなものを 簡単なJavaコーディングのみで作ることができます。
たとえば、ベースとしてJSPを使いつつもMixer2を活用したい場合、下の図のような使い方が可能です。

タグを新規に作る場合は目的のタグ型クラスを new すればよいのですが、 TagCreatorクラスでも可能です。 TagCreatorのほうがeclipse等のIDE上でコード補完が効くので便利です。
Div div = TagCreator.div();
Span span = TagCreator.span();
span.getContent().add("Hello World !");
span.setStyle("color:red;");
div.getContent().add(span);
String result = mixer2Engine.saveToString(div);
System.out.println(result);
output:
Hello World !
table タグを作る場合にはTableBuilderクラスが便利です。
String[][] data = new String[][]{
    // id, name, price
    {"1", "cookie", "2.50"},
    {"2", "candy",  "3.75"}
};

TableBuilder tBuilder = new TableBuilder();
for (String[] row : data) {
    tBuilder
        .addTr()
        .addTd(row[0])
        .addTd(row[1])
        .addTd(row[2]);
}
Table table = tBuilder.build();
String str = mixer2Engine.saveToString(table);
System.out.println(str);
output:
1 cookie 2.50
2 candy 3.75
For more detail of TableBuilder, see Mixer2 official site.
いかがでしたか? できあがったタグオブジェクトをstring化したあとなら jspやvelocityに埋め込むこともできます。 Mixer2と他のテンプレートエンジンの共存は十分に可能です。 ぜひ組み合わせ技も考えてみてください。

Sunday, September 22, 2013

Google Maps API with Mixer2, SpringMVC

Mixer2 - the java/xhtml template engine - is compliant to inline JavaScript. https://github.com/nabedge/googlemap-mixer2-sample is a sample project using Google Maps API, Spring MVC, and Mixer2. I describe the code and some tips.

First, look at the map.html, a template html file for Mixer2. This html is almost a copy of google official hello world sample.

The difference is one code.

before:

center: new google.maps.LatLng(-34.397, 150.644),

after:

center: new google.maps.LatLng(defaultValues.lat, defaultValues.lng),

and one script tag is added.


The point is that the "data" and "program" is separated into each <script> tags.

Next, the controller of Spring MVC, IndexController. Controller create the model having start latitude and longitude values and pass it to view;

    @RequestMapping(value = "/")
    public String map(Model model) {
        log.debug("showing map()...");

        Map latLngMap = new HashMap();
        latLngMap.put("lat", 35.633302);
        latLngMap.put("lng", 139.799652);
        
        model.addAttribute("latLngMap", latLngMap);
        
        return "mapView";
    }

The view is MapView class.

import com.google.gson.Gson;

// snip...

// load html template
String mainTemplate = "classpath:m2mockup/m2template/map.html";
Html html = getMixer2Engine().loadHtmlTemplate(
        resourceLoader.getResource(mainTemplate).getInputStream());
        
// snip...

Map latLngMap = (Map) model.get("latLngMap");

// set default value for google maps api
Gson gson = new Gson();
Script script = html.getHead().getById("defaultValuesJson", Script.class);
script.setContent("var defaultValues = " + gson.toJson(latLngMap) + ";");

You should look that the content of <script id="defaultValuesJson"> tag is replaced by the new json strings using mixer2 tag object and methods.

You get the result of browser:


.....

THE POINTS:

  • You can write map.html on local editor. The JavaScript code can run without deploying application server. You can see the map on browser. If you use JSP and write the code like "center: new google.maps.LatLng(<%=startLat%>, <%=startLng%>)", Your code can not run it as JavaScript !
  • Using Mixer2 as template engine, you should separate "data" and "program" in your JavaScript code on the template and put them into each <script> tags. On the server side, replace only <script> tag having the "data".

Tuesday, September 10, 2013

html5j&JJUGの勉強会でLTしてきた(資料うpあり)

疲れてる&軽く酔ってるので資料うpと、自分なりのメモだけ。

うん、Web標準とHTML5っていうテーマをみんな無視しすぎだと思った。まあ堅いこと言ってもしゃーないけどさ。

ということでやったLTがこちら。

この資料だけじゃ、5月のJJUGでのプレゼンや、1月のSpring勉強会でのプレゼンの焼き直しにすぎないですごめんなさい。肝心なのはこの前半にデモしたWebアプリです。 そのソースコードはこちら

もっと詳しく聞いてやってもいいぜ、という方は、今月末の第三回 #渋谷Java : ATNDへお越しください。それまでの間は「Mixer2によるダイナミックCSS Sprite」に関する全貌の細かい話はブログ等には書かないつもりです。

そうはいっても、それぞれスケジュールってものがありますので今日の勉強会だって今月末のだって、来れない人だっていますよね。 今日もLT後にいろいろ質問してくださった方がいらっしゃいました。それはそれで大歓迎なんですが、 どうぞメーリングリストのほうもご利用ください。ちょっとした疑問でも、漠然とした質問でも。

Mixer2がらみじゃないことでも、今夜はもうひとつ、いいことがあった気がします。いい気分。おやすみなさい。

Sunday, July 28, 2013

JavaとキャッシュとMixer2

激辛担担麺ランチ食ったら目が覚めるどころかかえって猛烈な睡魔に襲われております。

さて、今日はキャッシュ技術の話。先に言っておきますが、すごく長いです。そして大した結論ではないです。

ソフトウェア、ハードウェアの性能向上にはキャッシュ技術は欠かせません。これは開発言語も分野も問いません。 CPUにだってキャッシュ的な機能要素はあります。WebアプリではDBの検索結果をキャッシュして使いまわすことで DBの負荷を下げ全体の性能を上げるというのは常套手段です。

キャッシュとkey-valueストアとがごっちゃに論じられる場合もあります。実質的には同じようなことなのでどうでもいいんですけどね。 key-valueストアの代表格の一つにmemcachedがありますが、読んで字のごとく"キャッシュ"ですし。

これだけ便利で普及しているキャッシュという技術ですが、いざ使おうとすると結構面倒です。 どんどんキャッシュにつっこんでいたらキャッシュストアがあふれてしまって容量オーバー。よくある話です。 古いものから自動的に消えるようになってるから容量オーバーの心配無しとか言ってたら、 それがキャッシュヒット率の低下を招いていしまってキャッシュ機構の存在意義がなくなったり。 本物のデータのほうを変更したにもかかわらずキャッシュのデータのほうを変更あるいは削除しそこねてバグったり。 また、キャッシュしたいモノとそれにひもづけるキャッシュキーは正確に一対一でないと 間違ったキャッシュデータをキャッシュに保存し、 あるいは間違ったデータをユーザーにお届けしちゃうことになります。 何をキャッシュキーにするか?は慎重に考えねばなりません。

Javaアプリの場合はさらに面倒なことがあります。

キャッシュしたくなるようなデータは、多くのケースでは単なるString(例:JSON形式)だったりintやlongだったりします。その場合ディープコピーかシャローコピーかを意識する必要がありません。しかしその調子でやると、Javaクラスのインスタンス=たとえばいわゆるPOJO的なもの=をキャッシュ対象とする場合の落とし穴に気づかないことがあります。

二人のユーザー(≒二つのスレッド)が同時に同じデータをキャッシュから取得しようとしたとき、 キャッシュは二人に「同じデータ」を返します。 しかしJavaの場合は変数のコピーは基本的にリファレンス(参照)に過ぎないので、 キャッシュから受け取ったデータを一人が加工するともう一人のほうに渡されたデータも変わってしまいます。不思議と言えば不思議、当然と言えば当然の現象です。 それをよしとする要求ならそれでよいのですが、Mixer2のhtmlテンプレートロード機能の性能UPのためにキャッシュを使うという要求なのであれば話は別です。

AさんとBさんがほんの数マイクロ秒差で同じページにアクセスしたとしましょう。Aさん用画面を処理するスレッドがキャッシュから取得したHtml型インスタンスに「こんにちはAさん」を埋め込んだ直後、別スレッドがキャッシュから受け取った同じインスタンス(のリファレンス)に「こんにちはBさん」を埋め込んでしまう可能性が発生するのです。それはまずい。

Javaで実装されたキャッシュ機構として有名なものにEHCacheがあります。そのドキュメントにはこんなことが書いてあります。日本語訳by私なのであまり信用しないように(笑)

Ehcache | Documentation | Using Ehcache

Copy Cache

A Copy Cache can have two behaviors: it can copy Element instances it returns, when copyOnRead is true and copy elements it stores, when copyOnWrite to true.
コピーキャッシュパターンには二つの方法があります:

  • copyOnReadがtrueのとき、エレメントのコピーを戻り値として返す。
  • copyOnWriteがtrueのとき、エレメントのコピーをキャッシュに保存する。
注:エレメント≒キャッシュに保存しているデータのこと

A copy on read cache can be useful when you can't let multiple threads access the same Element instance (and the value it holds) concurrently. For example, where the programming model doesn't allow it, or you want to isolate changes done concurrently from each other.
同じエレメントのインスタンス(とそれが内部に持つ値)に対し、複数のスレッドがアクセスすることを許可したくない場合、キャッシュ読み込み時のコピー設定を使うとよいでしょう。たとえばプログラミングモデルがそれを許さない場合や、エレメントを確実に隔離しつつ同時発生的な変更を可能にしたい場合です。

Copy on write also lets you determine exactly what goes in the cache and when. i.e. when the value that will be in the cache will be in state it was when it actually was put in cache. All mutations to the value, or the element, after the put operation will not be reflected in the cache.
キャッシュ保存時のコピー設定を使うと、どんなデータがいつキャッシュに保存されたのかを厳密に把握することができます。キャッシュに保存されているものは保存した時点の状態のままです。エレメントに対するどんな変更も、それがキャッシュへのput処理の後のことなのであればキャッシュの中のエレメントには反映されません。

A concrete example of a copy cache is a Cache configured for XA. It will always be configured copyOnRead and copyOnWrite to provide proper transaction isolation and clear transaction boundaries (the state the objects are in at commit time is the state making it into the cache). By default, the copy operation will be performed using standard Java object serialization. We do recognize though that for some applications this might not be good (or fast) enough. You can configure your own CopyStrategy which will be used to perform these copy operations. For example, you could easily implement use cloning rather than Serialization.
コピーキャッシュパターンの具体例はXA(分残トランザクション)向きに設定されたキャッシュでしょう。 copyOnReadとcopyOnWriteを常にオンに設定することで、適切なトランザクション隔離と明確なトランザクション境界を提供します (コミット時のオブジェクトの状態はキャッシュに入れるときの状態と同じであるということ)。 デフォルトでは、コピー処理ではJavaオブジェクトのシリアライゼーションが使用されます。もちろん、アプリケーションによってはそれは良い(or速い)方法ではないことを、我々は認識しています。独自のCopyStrategyを設定しておけば、お望みのコピー処理方式を使用することができます。 たとえば、シリアライゼーションではなくクローン処理を使うように簡単に設定できます。

Ehcache | Documentation | Configuration

copyOnRead and copyOnWrite cache configuration

A cache can be configured to copy the data, rather than return reference to it on get or put.
キャッシュからgetまたはputするときにデータのリファレンスではなくコピーを使うかどうかを設定できます。
(途中略)
The default configuration will be false for both options.
デフォルトではcopyOnRead,copyOnWriteの両方ともfalseになっています。

長々と引用してしまいましたが、重要なポイントがさらりと書いてあります。

  • コピーを使うように設定したとしても、ほっとくとserializationを使うので場合によっては性能が悪かったりちゃんとディープコピーできなかったりするかもですよ。ちゃんとCopyStrategyも実装/設定してね。
  • デフォルトでは、ディープコピーじゃなくてリファレンスを、キャッシュから返したりキャッシュに保存したりしますよ。

EHCacheがイケてないということではもちろんありません。 このへんが汎用キャッシュライブラリとしてできる限界なのでしょう。 CopyStragetyパターンに沿ってディープコピーの方式を好きに実装&設定できるのですからむしろ高機能とも言えます。

ところで、キャッシュサーバやそのクライアントライブラリは今やいろんな実装があります。 しかしコレといった共通の規格がありません。そこで、Java言語用の共通APIが規格されつつあります(2013/7現在まだドラフト)。 それがJSR-107 JCacheです。ちなみに、JSR-107の中の人はEHCacheの中の人のシャローコピーです。

さて、ここでようやくMixer2の話になります。

テンプレートエンジンの多くがキャッシュ機能を持っています。velocityしかり、thymeleafしかりです。 同じ画面を表示するために同じテンプレートをパースする処理を繰り返すくらいなら、 パース処理後の状態をキャッシュに保存しておけばそのぶんの処理を省略できる=性能がよくなる、という寸法です。 多くの場合はキャッシュエンジンの内部にMapかなにかでキャッシュを維持しているだけです。

もちろん独自実装も使えるようにするためにインターフェイスを提供していることもあります。

Mixer2もこれらにならって独自のキャッシュインターフェースを提供することも考えたのですが、あえてそうはしませんでした。 代わりに選んだのがJSR-107 JCacheです。このinterfaceクラスを持っているjarファイル名で言うとcache-api-0.8.jar(数字はバージョンなので変わる可能性あり)です。

先日リリースしたmixer2-cachableでは、 EHCacheのような汎用キャッシュライブラリを使うか、あるいは独自に実装するかによらず、とにかくjavax.cache.Cache(通称JCache)として提供されるinterfaceを実装したクラスをプラグインして使えるようになっています。また、同梱したCache実装をデフォルトで自動的に使用可能なので、それでよければそれ以上頭を使う必要もありません。

CachableMixer2Engineクラス経由で提供されるloadHtmlTemplate()やその種のメソッドは、設定したCache実装を通してhtmlテンプレートのロードが可能です。何より重要なのが、必ず、キャッシュのget/putの過程ではHtml型インスタンスのディープコピーを使うように実装済みであることです。しかも、高速かつ正確なcopy()メソッドを使っています。

これによって、たとえばEHCacheを使ったときでもcopyOnWrite/copyOnReadのon/offに関係なく確実に高速ディープコピーが使われるので、キャッシュ上のデータを安全かつ高速に使うことができます。CopyStrategyに沿って実装をどうのこうのと考える必要もありません。

また、CacheableMixer2Engineはロードしようとするテンプレートそれ自体のSHA-1ハッシュを計算してそれをキャッシュのキーとして使っています。キーの生成方法としてはとても安全なのですが、実はそれが性能上の欠点でもあります。せいぜい数十kbyte程度であろうHTMLテンプレート文字列のSHA-1を計算すること自体は大したコストではないのですが、テンプレートがFileに入っている場合、それを全部String等に読み込むコストが発生してしまうのです。 しかしこれは、Mixer2のテンプレートロード機能は、テンプレートの型としてjava.io.FileのほかInputStreamにもStringにも対応しているので、どれであっても同じようにキャッシュ(のキー)を扱えるようにするための措置でもあります。このあたりは今後、キャッシュキー生成の高速性と安全性と簡易性を両立させるうまい実装が思いつけばどうにかなるかもしれません。次のバージョンアップにご期待ください(いつだ?)。

ところで、キャッシュ機構といえばこんなのもあります。

つまり、DIコンテナが、というか、Javaアプリケーションフレームワーク自体がキャッシュ機構を提供しているケースです。 こういうものとMixer2を組み合わせる場合はmixer2-cacheableなんてほっといてフレームワークに合わせるのもよいでしょう。

そんなわけで、Mixer2のテンプレートロード機能にキャッシュを使いたい場合は、以下の3つのパターンにまとめることができます。

  • mixer2-cacheableをデフォルトのまま使う。数十kbyte程度の小さなテンプレートが数百種類、という程度の要求仕様であれば十分でしょう。
  • mixer2-cacheableを使いつつ、cacheオブジェクトはJCache(JSR-107)を実装した別のものを使う。CacheableMixer2Engine.setCache(別のキャッシュインスタンス) のようにすれば差し替えられるようになっています。(DIコンテナでの設定例はこちら
  • mixer2-cacheableは使わない。Mixer2Engineを継承した別クラスを独自実装し、そこにキャッシュ機構を組み込む。あるいはアプリケーションフレームワーク提供のキャッシュ機構を経由するようにする。

そして究極の選択は、テンプレートのキャッシュなんて気にしないことです。Mixer2のテンプレートロード機能は結構速いです。 かなり大きなテンプレートであっても一桁ミリ秒で済んでしまいます。ならば、100msec単位のコストがかかりがちなほかの機能(外部通信、DB検索等)をチューニングするほうがよほど性能向上を見込めるでしょう。

Saturday, July 27, 2013

mixer2とSpringMVC用のViewResolver

mixer2-cachableとともにmixer2-1.2.10もリリースしました。

mixer2-cachableやその他のキャッシュ実装との連携をしやすくするための手直しが入っているほか、 もう一つ重要な機能が追加されています。 SpringMVC用のViewの基底クラスと、ViewResolverです。

これまで、SpringMVCでmixer2を使う場合にはControllerクラスの各メソッドでHtmlを組み立ててMixer2XhtmlViewに仕込んで直接返す、つまりメソッドの戻り値の型がMixer2XhtmlView、という方法を推奨していました。去年書いた下記の記事なんか、もろにそれです。

ところが、この方式、コントローラ自体がビューのテクノロジに依存することになってしまうため、ビューの差し替えが難しくなる=MVCの分離が容易というSpringMVCの良さを殺してしまう=ということになります。

SpringMVCのコントローラのメソッドの戻り値は、

  • Viewの名前≒ViewResolverが解釈しうるString値
  • ModelAndviewまたはそれを拡張したクラスのインスタンス(org.springframework.web.servlet.ModelAndView)
  • 独自のView実装(つまりorg.springframework.web.servlet.Viewをimplementsしたもの)のインスタンス
のどれでもかまわないという柔軟性の高さは素晴らしいです。しかしそれに甘えすぎるのもよくないわけです。

そこで登場するのがViewResolverインターフェイス(org.springframework.web.servlet.ViewResolver)です。 これをきちんと実装すると、コントローラはViewではなくViewの名前をStringで返すだけでよくなります。あとはViewResolverにおまかせです。

今回、こういったことをきちんと解決するために、 Mixer2XhtmlViewResolverAbstractMixer2XhtmlView をそれぞれ追加しました。使い方はそれぞれのjavadocの冒頭にあるとおりです。

また、SpringMVC用のアーキタイプで自動生成されるHelloWorldプロジェクトや、 フルーツショップサンプルアプリケーションも、すでにMixer2XhtmlViewResolverを使った形にリファクタリングされています。合わせて参考にしてください。

mixer2-cacheableをリリースしました。

mixer2-cacheableをリリースしました。 ソースはこちら。 ドキュメントはjavadocくらいしかないので、とりあえずここでざっくり書いときます。

maven使いの方はこんな感じ。 (追記:cache-apiとmixer2本体も別途指定が必要ということを忘れてましたので今朝加筆。) それ以外の方はいつものようにhttp://mixer2.org/dist/にあるアーカイブを使ってください。 mixer2-cacheable-1.0.1-jars.zip にはmixer2本体や依存jarも同梱されています。

単なるMixer2Engineのラッパーなので、使い方は簡単です。 いままでorg.mixer2.Mixer2Engine を使っていた部分を CacheableMixer2Engine を使うようにするだけです。

たとえばSpringMVC上でMixer2EngineをこんなふうにDIしていた場合、
変更前:
変更後:
とするだけです。

エンジンが読み込もう(loadHtmlTemplate/checkAndLoadHtmlTemplate)とするxhtmlテンプレート文字列のSHA-1ハッシュ値が自動的にキャッシュのキーとして使用されます。

デフォルトでは、同梱のorg.mixer2.cacheable.SimpleCacheが使用されます。 これはjavax.cache.Cache(JSR-107)仕様に対するごく簡単な実装になっています。 キャッシュの格納先としてConcurrentHashMapを使っているだけです。

JSR-107準拠のキャッシュ実装に差し替えることももちろん可能です。 たとえばSpringMVCを使っている場合、 これでCachableMixer2Engineに指定のキャッシュ実装のインスタンスがDIされます。

気になる性能ですが、これはhtmlテンプレートのサイズによってだいぶ異なります。 だいだい2倍から4倍程度の性能向上は見込めるでしょう。 ただし、mixer2がテンプレートのロードで頼っているJAXBのunmarshalerそれ自体が結構速いので、 たとえば80msecかかっていた処理が10msecになるみたいな劇的なことにはなりにくいです。 もともと6msec程度で済んでいたテンプレートロードが2msecになるとか、そのくらいの効果ととらえるほうがよいでしょう。

Friday, July 26, 2013

syntac highligher 壊れた

このブログのsyntax highliterのセッティングをおかしくしてしまったみたいです。コードのサンプルを貼り付けた記事がなんかぐちゃぐちゃに。。。

Monday, July 22, 2013

mixer2 version1.2.8をリリースしました

mixer2 1.2.8をリリースしました。 このリリースでは、Mixer2Engineに新たに checkAndLoadHtmlTemplate() メソッドを追加しました。

大きな違いはテンプレートのロードに失敗したときの挙動です。たとえば、 本来なら<br />と書くべき改行タグを<br>と書いてしまったxhtmlをロードしようとすると、

  • 従来のloadHtmlTemplate()は、nullを返す。例外情報はログに出るだけ。 これはテンプレートは正確なものが渡されるという前提の場合に有用です。
  • checkAndLoadHtmlTemplate()は、Mixer2JAXBExceptionをスローする。 これはテンプレートが間違っている可能性も考慮しなければならない場合に有用です。

Mixer2JAXBExceptionは単なるJAXBExceptionのラッパーです。 xhtmlのパースエラーの情報を得やすくするために、いくつかのメソッドを追加してあります。 詳しくはjavadocをご覧ください。

Tuesday, July 16, 2013

mixer2 version1.2.7をリリースしました

version 1.2.7をリリースしました。mavenのセントラルリポジトリからいつでもダウンロード可能です。 pomの書き方はこちら。 非maven使いの方のためのzipもいつもどおりこちらからダウンロード可能です。

1.2.7は次のような特徴があります。

  • copy()メソッドの高速化。
  • 新たにjaxb2-basics-runtime-0.6.4.jarへの依存性の発生
  • commons-langへの依存の廃止

copy()メソッドの高速化により、タグ型オブジェクトの使いまわしがより高速化されます。 また、loadHtmlTemplate()した結果のHtml型インスタンスをそのままcopy()して使いまわせば、 テンプレートキャッシュ機能の実装も簡単でしょう。 簡易的な性能測定では、中程度の量のhtmlテンプレートをloadHtmlTemplate()するのと、 そのHtml型インスタンスをcopy()するのとでは、copy()のほうが5倍から6倍程度早いという結果が出ています。

ただし、clone()メソッドやcopyTo()メソッドは使用しないようにしてください。 html5のaria-*属性やdata-*属性をうまくディープコピーに含められないというバグがあります。 copy()メソッドはそのようなバグはありませんのでcopy()を使用してください。

Monday, July 15, 2013

mixer2のサンプルアプリのプロジェクト分割バージョン

大きなWebアプリをチーム開発する場合、プロジェクトの分割がポイントになります。 そのためにServlet3.0のWeb fragmentのような仕様もあるのですが、 JSPを使う限り、htmlをjspに置き換えるという面倒な作業はまぬがれません。

mixer2をうまく使うと、プロジェクトの分割も比較的容易になります。 そんな話を今年1月のSpring勉強会にてお話した のですが、そのとき、出来上がっていたはずのデモの起動に失敗するという失態(笑)。

そんな失敗を糧に、改めてちゃんと動くように作った(はず)のがこちらです。 https://github.com/nabedge/mixer2-sample/tree/master/m2fruitshop mixer2-sampleリポジトリをcloneして、その内部のm2fruitshop配下の各ディレクトリを、mavenプロジェクトとしてIDE(eclipse等)にインポートしてください。m2fruitshop-webを起動すれば動くはずです。

少々説明が雑ですが、お暇な方はチャレンジしてみてくださいね。

Tuesday, July 9, 2013

mixer2のsnapshotバージョンの使い方

mavenプロジェクト限定ですが。

mixer2の正式バージョンはmavenのセントラルリポジトリに登録されていますので、 pom.xmlファイルにdependencyタグを書けば、特に何も考えなくてもjarとその依存jarは自動的にダウンロードされてくれます。

しかし今回のようにバグが発覚し(汗)、それを直している最中のSNAPSHOTバージョンを使いたい場合は、 pomにsnapshot用リポジトリを書いておけばよいです。こんな感じです。


    4.0.0

    com.example
    project-name
    0.0.1-SNAPSHOT

    
        
            sonatype.oss.snapshots
            Sonatype OSS Snapshot Repository
            http://oss.sonatype.org/content/repositories/snapshots
            
                false
            
            
                true
            
        
    

    
        
            org.mixer2
            mixer2
            1.2-SNAPSHOT
        
    

2013年7月現在、mixer2は1.2系統として開発されています。mixer2では、pomのversionタグをいちいち書き変えるのが面倒なので、 x.y.z-SNAPSHOTではなくx.y-SNAPSHOTの形式で開発を続けています。 いまの最新正式バージョンが1.2.6ですので、現在の1.2-SNAPSHOTは1.2.7になる予定の最新開発バージョンだと思ってください。

Sunday, July 7, 2013

mixer2-1.2.6でhtmlタグのオブジェクトのディープコピーの性能が向上しました

追記
1.2.6がバグってました。すいません。1.2.6のcopy()やclone()メソッドは、html5タグのdate-*属性やaria-*属性をうまくコピーできません。 他の一般属性は大丈夫です。直し方のアイデアはあるのですが、ちょっと時間が取れません。7月中にはなんとかしますので、 それまでは1.2.5以前をご利用ください。はー、テストが甘かった。反省します。

mixer2-1.2.6をリリースしました。数時間後にmavenセントラルリポジトリから使用可能になります。 maven使わない派の方のためにjarのセットがこちらにあります

リリース頻度が早すぎるので少し落ち着かせる、と宣言してから1か月ちょいしかたっていませんが、 気になっていた部分の改善ができてしまったので、そこは、まあ。(^^;

気になっていたのはcopy()メソッドの性能です。これまではよく見かけるserialize/deserializeを駆使した手法を使っていたので、どうしてもディープコピー処理に時間がかかっていました。 1.2.6では、各htmlタグにマッピングされる全てのクラスにclone()、hashcode()、equals()メソッドが追加されています。 従来のcopy()メソッドは内部ではclone()を呼び出しているだけになっています。もちろん正確にディープコピーします。

これにより、htmlテンプレートをloadHtmlTemplate()した結果のHtmlオブジェクトをキャッシュしておき、二回目以降はキャッシュのディープコピーで済ませることで、アプリケーションの性能を高めやすくなります。従来はcopy()メソッドが重かったため、どんなキャッシュ機構を作っても大した効果は得られませんでした。

ごく簡単なキャッシュ機構を作って試したところ、いちいちloadHtmlTemplate()するよりも30%前後は性能がよくなりそうです。もちろんhtmlテンプレートの内容によって数値は異なるでしょう。また、htmlテンプレート全体ではなく、その一部のタグをcopyしながら使いまわすような処理を書く場合でも、この性能向上の恩恵は受けられます。

それから、キャッシュキャッシュ言ってますが、逆に、1.2.5まではあった、javax.cacheインターフェースを使ったキャッシュ機構を、1.2.6では完全に廃止しました。@depricatedだったメソッドも完全に削除されています。これはキャッシュ機構はmixer2本体に持たせるよりも、使う側で独自に作ってもらうほうがよいと判断したためです。mixer2が依存するjarは本当に必要最低限にしておくほうがライブラリとして使いやすいはずです。 もしやるとしても、たとえばmixer2-cachewrapperみたいな別プロジェクト/ライブラリとしてキャッシュ機構を付加したラッパーライブラリを提供する形になるでしょう。(誰か作ってください^^;)

さて、さすがにもう眠いんで寝ます。

Sunday, May 19, 2013

mixer2-1.2.3をリリース

mixer2-1.2.3をリリースしました。

特に大きな機能追加はありません。逆に、キャッシュ機構はすべて@Deprecated(廃止予定)にしました。 1年前の1.1.3でJSR-107のcache APIを使えるようにメソッド等を用意したものの、 JAXBのアンマーシャラーだけでけっこう性能が出るっぽくて、 キャッシュ使うとそのコストとどっこいになっちゃうみたいだぜ? という出オチをひっくり返すことが結局できませんでした。

まあ、よいことなんだと思います。 で、無理にmixer2本体にcache-API対応メソッドを実装してコードを肥大化させてしまうよりは、 早めに削るという判断をしてみました。 mixer2を使う側で好きなキャッシュ機構を使ってラップしてもらうほうがよいと思ってます。

@Deprecatedにしたもののメソッドはまだ残っています。 なので、cache-api-0.5.jarへの依存性はまだ消してないですが、そのうち消します。 そうなると、mixer2が依存するライブラリはcommons-lang,commons-beanutils,commons-loggingだけになります。

なお、ここんところリリース頻度が早すぎる気がして反省してます。 本体で気になっている部分はだいぶ直せたので、 今後はここ数か月ほどのリリース頻度ではないと思います(バグがあったらすぐ直しますけど)

なお、ドキュメントの加筆は今後もちょくちょく続けようと思います。

Sunday, April 21, 2013

JJUG CCCにて講演予定:「さらばJSP – JAXBとテンプレートエンジンmixer2」

5月11日(土)に新宿で開催される、日本Javaユーザーグループのクロスコミュニティカンファレンス(以下JJUG CCC)にて講演します。 演題は「さらばJSP – JAXBとテンプレートエンジンmixer2」です。

さらばJSP、とか勢いでぶちあげちゃった感アリアリですがw、 とりあえずGW中にじっくり資料考えるつもり。

追記

無事、講演を終えました。40名様ほぼ満席。関係者のみなさん、本当にありがとうございました。

Thursday, March 7, 2013

mixer2-1.2.1リリース

mixer2 version 1.2.1をリリースしました。 htmlテンプレートの改行が取り除かれてしまうバグが解消されています。 このバグ対応は、利用するアプリの側からすると従来の挙動が変わるのと同等の影響がありうるため、versionは1.1ではなく1.2系統としました。 といっても1.1系統のメンテは停止する可能性が高いですが。

version1.1.xではインラインのjavascript内部もやはり改行が取り除かれてしまいましたが、1.2系はそういうことはありません。 ただ、xhtml的に正しい書き方をしなければならないことに変わりはないので、コメントアウトとCDATAを組み合わせて書く必要があります。 たとえば、GoogleAnalyticsを使うためのインラインjavascriptであれば、テンプレートにはこんな風に埋め込んでください。


2行目と14行目がポイントです。全体として、javascriptとして正しいのでちゃんと動きますし、xhtmlとしても正しいのでmixer2でテンプレートとしてロードすることも可能です。

SpringMVC用のviewクラスも同梱しています。 使い方はjavadocか、あるいはmavenアーキタイプでできるコードを参考にしてください。

Sunday, February 17, 2013

SpringMVCとmixer2を組み合わせて使うためのmavenアーキタイプを公開しました

WebアプリケーションフレームワークとしてのSpringMVCの完成度は非常に高く、 そこにテンプレートエンジンとしてmixer2を組み合わせると鬼に金棒です。

ところが、開発のスタート地点となる「最初のプロジェクト」を作るのが意外に面倒です。 mixer2でJSPレスなSpringMVCアプリケーションを作ってみたでお話ししたFlower shop sample appを自分のプロジェクト用の土台に作り替えるという手もあります。が、プロジェクト名やGAV情報の変更、Javaのパッケージ名の変更、いらないコントローラやテンプレートの削り落としなどの作業が必要になります。

そこで、ある程度の設定ファイルと最低限のHelloWorld的な画面をひとつ、 初めから組み込んであるプロジェクトを簡単に作れるmavenアーキタイプを作りました。 任意のプロジェクト名、GAV情報(mavenのgroupId,artifactId,versionのこと)、javaパッケージ名で新規プロジェクトを作ることができます。 しかもSpringMVCとmixer2に必要な設定ファイルとごく基本的なヘルパークラスまで含まれています。

詳しい使い方は公式サイトの mixer2 - Maven archetype for SpringMVC and mixer2 に書いてあります。お試しください。

SpringMVC+Mixer2でデザイナーとプログラマが仲良く仕事できるWebアプリ開発プロジェクトを、あなたにも!

えっ?いまだに生のservletとjspだけでWebアプリを作っている? ありえません。かなり重症ですね...。

Monday, February 11, 2013

Spring勉強会にて講演しました

先月、JSUG(Japan Spring Users Group)の勉強会にて講演の機会をいただきました。下のが講演資料です。

意外にも会場(50人くらい?)が満席!実際ATNDの申し込み画面でもキャンセル待ちが出てたようです。 つたない講演で満足いただけたかわかりませんが、足を運んでいただいた皆様には感謝です。 土岐さん、長谷川さんほかSpringユーザー会の皆様、会場を貸してくださっているVMWare社さん、本当にありがとうございました。 見に来てくれた新旧の同僚の方々、軽くヤジってくれたってよかったんだぜw。

Sunday, January 20, 2013

JUnitとDbUnitとSelenium

メモがてら。

DBUnitを使えば、excelで用意したテスト用のデータをテスト実行前にDBに投入するとかがラクになる。 ソースはここ

DbUnit用のデータセットはデータベースのテーブルへの出し入れだけにしか使えないということはない。 たとえばDBデータではない、テストのパラメータとかをテストコードの外で管理するために使ってもかまわない。ソースは上のやつ参照。

JUnitテストケース上で @Theories, @Thory, @DataPoint, @DataPoints といったアノテーションを使えば、パラメータ化テストが楽になる。詳しくは「JUnit実践入門」をアマゾンで買いましょう。