読者です 読者をやめる 読者になる 読者になる

Java EE 事始め!

主にJava EEについて、つらつらとマイペースに書いていきます。「Java EEを勉強するときに、一番最初に読んでもらえるブログ」を目指して頑張ります!

【書評】Spring Microservices(洋書)

www.packtpub.com

Spring Cloudの勉強をするために買いました。

内容としては、

  • Spring Bootの基礎
  • Microservicesとは何ぞや?その背景は?
  • Circuit BreakerやService Discoveryって何ぞや?なぜ必要なのか?
  • Spring Cloudの基礎
  • Docker、Mesos

など、かなり幅広いです(ページ数もそこそこ多い)。

背景やなぜ必要なのか、というところはすごく参考になりました。 また、Spring Cloudでアプリケーションを作るところは、そこそこ多いマイクロサービス(最終的には10個くらいになる)を手順を追いながら作っていくので、手を動かして学びたい人にはうってつけな本です。

総じて良い本だと思いますが、注意点があります。

1つ1つの手順があまり詳しく書いておらず、「え?このソースコード、どのマイクロサービスの!?」と思うことがとても多いです。また、誤植も散見され、そのままだと動かんやろ、という部分もいくつかありました。

正直言って、Spring BootやSpring Cloudが全く初めてという方には向きません。ある程度は学習したことがある方が、より理解を深めるための本だと思います。

「新人研修や本では教えてくれないJava!」というタイトルで発表してきました #jjug_ccc

昨日のJJUG CCC 2016 Fallで発表してきました。ありがたいことに、CCC登壇は6回連続6回目になります。

speakerdeck.com

朝イチ10時から、かつ基調講演の裏番組にもかかわらず、120名くらいの部屋がほぼ満席となりました。

ご参加いただいた皆様、そして運営されていたJJUG幹事やボランティアスタッフの皆様、本当にありがとうございました。

また、感想ブログや全セッションのスライドは、こちらにまとまっています。

GitHub - jjug-ccc/slides-articles-2016fall: JJUG CCC 2016 Fallの発表資料およびブログ記事まとめ

発表内容

この発表をしようと思った理由

僕は研修トレーナーが仕事なので、毎年春には新人研修を担当しています。それ以外の期間は、新人以外の一般のお客様向けに、自社開催の研修だったり、お客様先で研修したりしています。

今回の発表のような内容は、通常、新人研修では扱いません。また、1つにまとまった書籍や資料が無いためか、新人でなくても意外とご存知ない方が多いと感じていました。

だったら資料を作って公開しちゃえ!と思ったのがきっかけです。

発表の最初に、「どちらかというと『初心者』だという方?」「どちらかというと『指導役』だという方?」と全体に対して伺ったところ、ちょうど半々くらいでした。

資料を公開したツイートは、かなり多くのリツイートやいいねをいただきました。

また、反応もいくつかいただけました。お役に立てたようで、とても嬉しいです!

ちなみに、Speaker Deckにアップしているスライドは、本来話したかった内容が全て含まれた「完全版」です。

ただ、想像以上にボリュームが膨らんでしまったため、発表はその抜粋版で行いました。発表を聞いていただいた方も、ぜひこの「完全版」をご確認ください。

この資料はPDFでアップしていますので、ダウンロードや社内でのご利用等は、ご自由にどうぞ!

著作権は所属会社であるカサレアルに属しますので、有償のサービスで使うことだけはご遠慮ください)

聴講したセッション

Java EE - What's Next? #ccc_a2

Oracleの方によるJava EE 8、Java EE 9の今後のプランに関するセッションでした。以前、JavaOne報告会で寺田さんがお話されていたので、それを再確認する感じでした。

冒頭、「Microservices」や「Cloud Native」という言葉が出てきたので、Oracleもこういうところに本腰を入れていくんだなあ、と改めて実感しました。

僕の理解では、本丸は2018年末に出るJava EE 9で、EE 8はその前フリ、という感じです。

MVC 1.0が(おそらく)標準では入らなくなることは残念ですが、Java EEが前進したので、個人的には全体的に満足しています。

余談ですが、Anilさんの英語は聞き取りやすく、ちょっとばかし自分の英語ぢからアップを感じ取れました。

でも、50分間は集中力が持たず、後半は通訳の方に頼りっぱなしでした。まだまだやなあ・・・。

Event Driven Microservices with Spring Cloud Stream #ccc_ab3

最近はSpring業務も多いので、槙さんのセッションは外せませんでした。

メッセージングとかキューは、最近ちょっと使っててとても面白いなあと思ったので、自分でもやってみたいです。

しかし本当に、Springは進んでいるなあ。Java EEも頑張ってほしいなあ・・・。

Spring CloudでDDD的なマイクロサービスを作ってみる #ccc_ab4

2016 SpringでThymeleafのセッションをされた椎葉さんのセッション。

僕はDDD本も読んだことないのでぼんやりとしか知らなかったのですが、ちょっとだけ見えたような気がします。

ソースコードも公開されていたので、後でじっくり確認してみます!

JAX-RS REST Client で Cognitive Service や Excel を操作しよう #ccc_cd5

Microsoft寺田さんのセッション。最近話題のCognitive Serviceを知りたくて参加しました。

音声・画像・動画・テキストから、顔や物体や感情を判定するサービスです。

非常に面白いなあと思いました。色々と活用できる場面がありそうですね。

JPA と DDD の関係で僕が思っていること #ccc_ab6

Qiitaで有名な@opengl_8080さんのセッションです。

公私とも、ずーっとこの方のQiitaにはお世話になっています。去年のCCC Fallで初めてお話ししたのでした。

JPAについて徹底的に調べられていて、それなりにJPA歴がある僕でも「え!?知らんかった・・・」ということが沢山ありました。

やっぱり、しっかり仕様を確認することと、実験することが大事ですね。

Payara Micro の設計と実装 #ccc_ab7

我らがGlassFish User Group Japan会長、蓮沼さんのセッションです。

GlassFishソースコードをフォークして、英国C2B2社がオープンソースで開発しているのが「Payara」です。そして、Payara Microはその組み込みサーバー版です。

このセッションでは、Payara Microのソースコードから、起動・クラスタリング・デプロイの仕組みについて解説されていました。

さすが蓮沼さん、知識が膨大だ・・・。でも、とても興味深かったです。

参加を終えてどう感じたか

マイクロサービスやクラウドネイティブというのが当たり前になりつつある、というのが率直な感想でした。

そして、来年のCCCのがある頃には、完全に「当たり前」のものになっているだろうと思います。

研修、そしてトレーナー自身も、進化を続けていかなければならないと、改めて思いました。

そして、聴くだけで終わらせるのではなく、どれか知った内容を今日から実践してみよう、とも思います。

槙さんが発表されていたSpring Cloud Streamをやってみたいなー!

運営に関して

改めて、JJUG幹事とボランティアスタッフのみなさま、ありがとうございました。そして、お疲れ様でした。

おかげさまで、非常に楽しい1日となりました。

運営が非常にスムーズで、特に問題点のようなものは見つけられませんでした。

敢えて言えば、参加者数が増えたことで、お手洗いが混んでいたなあ・・・というくらいでしょうか。それで次のセッションにちょっと遅刻しちゃったことが何回かありました。

セッション間の10分休憩を15分くらいにすると丁度いいかもしれません。午後に2セッションごとにある40分間の休憩も、体力回復のためとてもありがたいのですが、30分間くらいでも大丈夫と思います。そうすると、トータルでの終了時間(懇親会開始が19時半)は変わらないはずです。

(このことはアンケートにも記入済みです)

あと、聴きたいセッションの時間帯が重なってる!ということが多かったのですが、それはすなわち、どのセッションも魅力的であったということなので、これは致し方ないかと思います。

まとめ

久々に会えた方や、ツイッターでは知っていたものの対面でお話しすることは初めてだった方など、いろんな方にお会いできました。これもCCCの楽しいところですね。

来年も楽しみにしております!

【回避方法あり】Payara 4.1.1.164には認証に関するバグがありました

Java EE 7 Payara

環境

Payara Web ML 4.1.1.164 確認していませんが、Full Profileでも同じバグがある可能性があります。 また、4.1.1.163以前のバージョンには、このバグはありません。

現象

JDBCリソースやレルムの設定を正しく行っても、アプリケーションでフォーム認証ができません。 具体的には、ログイン画面から正しいID・パスワードを入力してログインしても、403エラーになります。

原因

「デフォルトロールマッピングプリンシパル」の有効化が認識されないようです。 これは、管理コンソール・asadminコマンド・domain.xml直接書き換え、いずれを行なってもダメでした。

いつ修正されるのか

来年第1四半期にリリース予定のPayara 4.1.1.171で修正される予定です。

回避策

アプリケーションのWEB-INFフォルダにglassfish-web.xmlというファイルを作成し、下記のように記述してください。 こうすると、うまく「デフォルトロールマッピングプリンシパル」の有効化が認識されるようになります。

<glassfish-web-app>
    <property name="default-role-mapping" value="true">
        <description>Enable default group to role mapping</description>
    </property>
</glassfish-web-app>

もっと詳しいことは

GitHubにIssueを書きましたので、ご覧ください。

403 error occurs when Form-Authentication succeeded PAYARA-1244 · Issue #1213 · payara/Payara · GitHub

サンプルコードはこちら。

GitHub - MasatoshiTada/form-authentication-sample

Spring Boot 1.4でThymeleaf 3を使う

Spring Boot Thymeleaf Spring Framework

プロジェクト作成は、Spring Initializrとかで作成済みの前提です。

Spring Boot 1.4では、デフォルトでThymeleaf 2が使われます。

Thymeleaf 2ではXHTMLで書く必要がありますが、Thymeleaf 3だと完全にピュアなHTMLで書くことが可能です。

pom.xmlのpropertiesに下記の記述を追加します。

    <properties>
        <!-- その他のプロパティは省略 -->
        <thymeleaf.version>3.0.1.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>
    </properties>

Thymeleaf 3のバージョンと共に、Thymeleaf Layout Dialectのバージョンも指定する必要があります。

これだけで完了で、あとはビルドしなおせばThymeleaf 3が使われます。

Bean定義なども記述する必要はなく、本当にこれだけでおしまいです。

Gradleの場合はこちらを参照してください。

Support Thymeleaf 3 · Issue #4393 · spring-projects/spring-boot · GitHub

Java EE 7を勉強する方法(2016年ver.)

Java EE 7

今年になって、Java EEの学習環境がガラッと変わりました。

2016年も半分以上過ぎてしましましたが(笑)、今からJava EE 7を勉強するにはどうすべきか、改めて書きたいと思います。

1. まずは作りながら学ぶ

以下3冊のいずれか1つで、Webアプリを作りながらJava EEの概要を理解しましょう。本屋さんで軽く立ち読みして、ご自分に合いそうな本を1つ選んでください。

www.shuwasystem.co.jp

www.shuwasystem.co.jp

www.shoeisha.co.jp

これらの本は、いずれもWebアプリを作りながらJava EEの基本を学ぶ、チュートリアルのような形式です。

「わかりやすいJava EE」と「パーフェクトマスター」は、サーブレットJSPの学習が終わったJava初心者の方向けです。ただし、RESTful Webサービスを作る技術「JAX-RS」の説明は、2冊とも無いので注意してください。

ある程度のJavaの経験(Webフレームワーク、DIコンテナ、ORマッパーを使った開発経験)があるならば、「Java EE 7徹底入門」が良いでしょう。この本には、JAX-RSの章があります。

2. ブログ情報を調べてみる

上記の入門書籍だけでは、実開発には足りない部分があると思います。

多くの方がブログやスライドを書かれていますので、ぜひ参考にしてみてください。

JSF

JSFでは、まずは菊田さん(@kikutaro_)のブログを読んでいただくのが良いでしょう。

JSF カテゴリーの記事一覧 - Challenge Java EE !

その他の読んでおくべき情報は、下記の記事にリンクをまとめておきました。

JSFを使うなら読んでおきたいリンクまとめ - Java EE 事始め!

JAX-RS

JAX-RSでは、まずはうらがみさん(@backpaper0)のスライドを読みましょう。

JAX-RS入門および実践

ブログはこちら。

この投稿のタグ JAX-RS — 裏紙

アクションベースMVC

Java EEの情報を調べていると、「JSFコンポーネントベースで、Strutsはアクションベースで・・・」という情報が見つかることも多いと思います。

アクションベースMVCは、現在はまだJava EE 7標準ではありませんが、使うこと自体は可能です。僕のスライドを読んでいただくのが一番良いと思います。

Java EEアクションベースMVC入門 #jjug_ccc #ccc_cd4 // Speaker Deck

CDI

上妻さん(@n_agetsu)のブログが良いでしょう。リンクは下記にまとめています。

CDI/JTAを使うなら読んでおきたいリンクまとめ - Java EE 事始め!

JPA

まずは、拙著スライドからどうぞ。

はまる!JPA(初学者向けライト版)
http://www.slideshare.net/masatoshitada7/jpa-46874399

より高度な内容については、下記のスライドを読んでみてください。

はまる!JPA
http://www.slideshare.net/makingx/jpa-29150059

金魚本に載ってないJPQLの話
http://d.hatena.ne.jp/megascus/20120925/1348575449

アプリケーションサーバ

サーバーそれぞれのエキスパートの方々がブログを書かれています。

GlassFish Japan

nekop's blog

yamadamnのはてな日記

Java EE全般

  • 上妻さんの資料

上妻さんがGlassFish勉強会で発表された、Java EEのパフォーマンスに関する資料です。

Java EE パフォーマンスTips #glassfish_jp

  • Java EE 7 Tutorial日本語訳まとめ

Java EE 7 Tutorialは、Oracle公式のチュートリアルです。全部ではありませんが、@kagamihogeさんが日本語訳をまとめていらっしゃいます。

The Java EE 7 Tutorialのテキトー翻訳まとめ - Qiita

  • ○○使い方メモ

@opengl_8080さんのブログです。Java EEに限らず、いろんな情報が書かれていてとても素晴らしいです。

特に、Bean ValidationとJTAの記事は必読です。

JavaEE使い方メモ(Bean Validation) - Qiita

JavaEE使い方メモ(JTA) - Qiita

各種Advent Calender

Java EE Advent Calendar 2015 - Qiita

Java EE Advent Calendar 2014 - Qiita

GlassFish Advent Calendar 2014 - Adventar

GlassFish Advent Calendar 2013 - Adventar

JBoss / WildFly (全部俺) Advent Calendar 2013 - Adventar

3. 仕上げは「パーフェクトJava EE」!

Java EE 7日本語書籍の決定版とも言うべき本です。内容の質・量とも素晴らしいです。開発でJava EEを使う方は必読でしょう。

gihyo.jp

4. 公式情報も確認しよう

かなり日本語情報が充実してきましたが、できれば英語の公式情報にも目を通しておきましょう。日本語情報が無い場合には、英語の1次情報をあたるのが最も良い方法です。OracleJava EE 7公式情報は、こちらにまとめられています。
Home: Java Platform, Enterprise Edition (Java EE) 7 Release 7

また、仕様書であるJSRも重要です。JSRはこちらから検索できます。「JPA JSR」のように「技術名 JSR」でググってもOKです。
The Java Community Process(SM) Program

まとめ

  • まずは書籍を読んで、手を動かしながらJava EEの基本を身につける!
  • 重要なブログ情報をチェック!
  • パーフェクトJava EEで仕上げ!
  • 公式情報もしっかりチェック!

もはや「Java EEは日本語情報が少ない」とは言えません。日本語情報は、現存の技術の中ではかなり充実している方だと思います。

ただし、日本語情報はGlassFish前提で書かれているものが多く、その他のサーバーでは細かい挙動が異なることも多々あります。

トラブル時に大事になってくるのは、英語の公式情報です。英語情報もしっかりとチェックしましょう。

それでは、Enjoy Java EE

【書評】「パーフェクトJava EE」は最強のJava EE 7リファレンス!

https://www.amazon.co.jp/パーフェクト-Java-EE-井上-誠一郎/dp/4774183164www.amazon.co.jpgihyo.jp

著者の1人である@kikutaro_さんから献本いただきました。ありがとうございます!

これまでのJava EE書籍の課題

まずは、Java EEの最新バージョンである「Java EE 7」対応の日本語書籍が少ないことでした。

なので、下記のEE 5本やEE 6本で勉強した後は、ブログ情報を検索したり、海外の英語情報に頼るしかありませんでした。

https://www.amazon.co.jp/マスタリングJavaEE5-第2版-DVD付-Programmer’s-SELECTION/dp/4798120545

https://www.amazon.co.jp/Beginning-6%7EGlassFish-3で始めるエンタープライズJava-Programmers-SELECTION/dp/4798124605

2014年暮れあたりから、徐々にEE 7対応の書籍も出始めました(下記)。

www.shuwasystem.co.jp

www.shuwasystem.co.jp

www.shoeisha.co.jp

これらはそれぞれ非常に良い本なのですが、3冊ともチュートリアル形式で、1つのアプリケーションを作っていく中でJava EEの機能を学習していくというスタイルの本です。

なので、初期学習には非常に良いのですが、「困った時に頼りになるリファレンス」というような本ではありません。リファレンスとなるような本は「マスタリングJava EE 5」しか無い、というのが現状でした。

待望の「Java EE 7で困った時に頼りになるリファレンス」が登場!

今回登場した「パーフェクトJava EE」は、待望の「リファレンスとして使える本」です。

内容はJSFJAX-RSCDIJPAが中心です(WebSocket・Bean Validation・JTAEJBも1章ずつ解説があります)。つまりJava EEのWebプロファイルですね。

それぞれがその技術のエキスパートによって書かれています(JSFは菊田さん、JAX-RSは井上さん、CDIは上妻さん、JPAは槙さん)。この技術だったらこの方だろう、という人ばかりですね。

内容の幅も深さも、他の書籍とは一線を画しています。

例えばWebSocketの章では、エンコーダーデコーダーを利用してJSON形式でデータをやり取りする方法が紹介されています(WebSocketってチャットアプリ作っておしまい、みたいな解説が多いですよね...)。

その他、CDIのクライアントプロキシ、JAX-RSのMessageBodyReader/Writerの性質、JSFのPrimeFaces、JPAのエンティティグラフやL2キャッシュなど。

その他にも重要な内容がたくさん書かれていますので、ぜひ読んでみてください。Java EEを開発で使っているすべての方必見の内容が詰まっています!

かなり高度な内容も書かれていますので、基礎知識なしで読むのはハードルが高いかもしれません。なので、上記3冊のチュートリアル形式の本をどれでもいいので読了してから、パーフェクトJava EEを読むことをお勧めします。

ちょっと気になった点

FacesContextのインジェクト(P.36)

@Inject
FacesContext facesContext;

このように書けるようになるのは、確かJava EE 8からだったと記憶しています。

Payara Web ML 4.1.1.162で上記のコードを試しましたが、デプロイ時にエラーになりました。

セッションIDの変更(P.352)

セッションIDを変更するためにHttpSessionの破棄および再生成を行っていますが、下記のようにHttpServletRequest#changeSessionId()メソッドを利用すれば、再生成の必要は無いと思います。

HttpServletRequest request = 
    (HttpServletRequest) FacesContext.getCurrentInstance()
        .getExternalContext().getRequest();
request.changeSessionId();

レルムの説明が少ない

レルムでの認証・認可は、サーブレットの章でサラッと数ページで説明されているだけでした。

レルムはあんまり使わないよ、というメッセージなのでしょうか?深読みし過ぎ?

最後に

著者の井上さん・槙さん・上妻さん・菊田さんには、本当に感謝の言葉しかありません。

これだけの質と量の書籍を書くという作業は、本当に大変だったと思います。

とても貴重な書籍を書いていただき、ありがとうございました!

Thymeleafのビューから@NamedなCDI管理ビーンにアクセスする

やりたいこと

こんなCDI管理ビーンがあって、

@Named
@SessionScoped
public class SessionDto implements Serializable {

    private String id;

    public String getId() {
        return id;
    }
}

Thymeleafのビューからこんな感じで参照したいです。

<p th:text="${sessionDto.id}">...</p>

本当は上記みたいにやりたかったのですが、現時点では、下記のような感じで参照することができました。

<p th:text="${#cdi.bean('sessionDto').id}">...</p>

以下、やり方を解説します。

名前からCDI管理ビーンを取得するクラスの作成

package com.example.rest.thymeleaf;

import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI;
import java.util.Set;

public class Cdi {

    public Object bean(String name) {
        CDI<Object> cdi = CDI.current();
        BeanManager beanManager = cdi.getBeanManager();
        Set<Bean<?>> beans = beanManager.getBeans(name);
        Bean<?> bean = beanManager.resolve(beans);
        Class<?> beanClass = bean.getBeanClass();
        return cdi.select(beanClass).get();
    }
}

引数のnameをもとに、ビーンを取得するメソッドを作ります。

まずBeanManagerを取得して、名前からSet<Bean<?>>を取得します。

名前だけではビーンを1つに特定できない(セッションスコープとかだとユーザー数だけ同じ名前のビーンがあるため)ので、resolve()でビーンを1つに特定します。

そこからビーンのClassオブジェクトを取得し、cdi.select(beanClass).get()でようやっと目的のCDI管理ビーンそのもののインスタンスを取得できます。

何か効率的ではないような感じがするので、今後変更するかもしれません。

参考資料

羽生田さんのスライド 3.Java EE7 徹底入門 CDI&EJB

かずひらさんのブログ CDIのBeanManagerを使う - CLOVER

CDI管理ビーンを参照するDialectを自作する

ThymeleafのDialectを自作します。

まず、IExpressionObjectFactory実装クラスを作成します。

この中で、先ほど作成したCdiクラスをnewしています。 ビューからは、getAllExpressionObjectNames()で返している名前でアクセスできます。

package com.example.rest.thymeleaf;

import org.thymeleaf.context.IExpressionContext;
import org.thymeleaf.expression.IExpressionObjectFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class CdiExpressionFactory implements IExpressionObjectFactory {

    private static final String EXPRESSION_OBJECT_NAME = "cdi";

    private static final Set<String> ALL_EXPRESSION_OBJECT_NAMES =
            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(EXPRESSION_OBJECT_NAME)));

    @Override
    public Set<String> getAllExpressionObjectNames() {
        return ALL_EXPRESSION_OBJECT_NAMES;
    }

    @Override
    public Object buildObject(IExpressionContext context, String expressionObjectName) {
        if (EXPRESSION_OBJECT_NAME.equals(expressionObjectName)) {
            return new Cdi();
        }
        return null;
    }

    @Override
    public boolean isCacheable(String expressionObjectName) {
        return false;
    }
}

次に、Dialectクラスを作成します。

IDialectというインタフェースがあり、そのサブインタフェースとして下記の5つがあります。

  • IProcessorDialect
  • IPreProcessorDialect
  • IPostProcessorDialect
  • IExpressionObjectDialect
  • IExecutionAttributeDialect

今回は、IExpressionObjectDialectを使いました。他の4つはまだ試せていません...(^^;

package com.example.rest.thymeleaf;

import org.thymeleaf.dialect.AbstractDialect;
import org.thymeleaf.dialect.IExpressionObjectDialect;
import org.thymeleaf.expression.IExpressionObjectFactory;

public class CdiDialect extends AbstractDialect implements IExpressionObjectDialect {

    private static final IExpressionObjectFactory EXPRESSION_OBJECT_FACTORY = new CdiExpressionFactory();

    public CdiDialect() {
        super("cdi");
    }

    @Override
    public IExpressionObjectFactory getExpressionObjectFactory() {
        return EXPRESSION_OBJECT_FACTORY;
    }
}

参考資料

Thymeleafのドキュメント Tutorial: Extending Thymeleaf

Thymeleafのソースコード

自作Dialectの追加

TemplateEngineをnewしている場所で、自作Dialectを追加します。

templateEngine = new TemplateEngine();
templateEngine.addDialect(new CdiDialect());

ビューの作成

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
      ...
<p th:text="${#cdi.bean('sessionDto').id}">...</p>

#cdi.bean()で自作したCdiクラスのbean()メソッドを呼び出しています。

引数には@Namedで指定したビーン名を指定します。

今回は@Namedvalue属性をしていていないので、「クラス名の頭文字を小文字にした名前」がビーン名になります。

まとめ

今回の方法は「Expression Object」(#で始まるやつ)を使ってみました。

この方法は非常に簡単でいいですね。他の方法も試してみたいです。

コードはこちら。

https://github.com/MasatoshiTada/jjug-action-based-mvc/tree/master/jjug-my-mvc