Java EE 事始め!

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

はじめてのSpringOne 事前準備編

初の海外カンファレンス参加!

エンジニアにとって、海外カンファレンスに参加することは憧れの1つかと思います。Javaエンジニアなら有名どころはJavaOneでしょう。

今回いろいろな幸運もあり、Pivotal社が主催しているSpringOne Platformに参加することになりました!

Spring Framework / Spring Boot / Spring Cloud / Cloud Foundryなど、Pivotalがリードしているテクノロジーの最新情報が聞けますし、最近は仕事でもこのあたりの技術を触ることが多いので、機会に乗って行くことにしました。

カンファレンスに申し込もう

SpringOneの公式ページから申し込めます。

https://2017.springoneplatform.io/ehome/s1p/registration2017.springoneplatform.io

お値段は1700ドルと安くはないのですが、早めに申し込むとやや安めになります。

また、更に安くなるディスカウントコードというのもあります。コードの取得方法は、興味があればTwitter DMや対面で聞いてください。

プレカンファレンストレーニングを受けてみよう

SpringOneは計3日間なのですが、その直前の2日間で特別なトレーニング(研修)を受けることが出来ます。

springoneplatform.io

2日間で700ドルと、通常のトレーニングに比べるととてもお得な値段になっています(もちろん、宿泊費とかは追加でかかりますが)。

内容は、僕も実施しているPivotal認定トレーニングとほぼ同じなのですが、Spring Boot 2.0やSpring 5など、最新技術が演習付きで学習できるのが特徴です。

僕は今回、「What's new in Spring 5」を受講することにしました。これを担当される講師の方が、いつも使っている認定テキストを作っているマスタートレーナーの方らしく、とても楽しみです!

宿泊などを手配しよう

必要なのはホテル・飛行機・保険の3つです。

今回はHISでまとめて手配しちゃいましたが、個別に手配してもよかったかな・・・と思います。

ホテルの注意点としては、危険な地域は避けることです。今回の会場であるサンフランシスコは比較的治安は良いようですが、それでも一般人は入るべきでない地域もあるようです。

危険な地域は、その地域に詳しい人(サンフランシスコならJavaOne経験者など)に相談するのがいいでしょう。もしくは、「地球の歩き方」という本に付属の地図に治安の良くない地域が書かれています。

地球の歩き方 ガイドブック B04 サンフランシスコとシリコンバレー2018年〜2019年版

飛行機は今回、JALの羽田・サンフランシスコ直通便にしました。香港経由便などを利用するともっと安くなるらしいのですが、僕はあまり海外旅行の経験が多くないので、万が一トラブルがあった場合や体力的なことを考えて、直通便にしました。

あと、HISなどを経由せず航空会社のWebサイトから直接申し込んだ方が、座席指定が出来たりメリットが大きいかな、と思いました。僕は体がデカイので、なるべく通路側の席が良かったりしますので・・・。

渡航手続きをしよう

ESTA

アメリカに短期間渡航する場合、ビザの取得は必要ありません。その代わり、WebからESTAの事前登録が必要になります。

https://esta.cbp.dhs.gov/esta/application.html?execution=e1s1

期限は渡航72時間前までらしいですが、時間がかかる場合もあるそうなので、なるべく早く登録しておいたほうがいいでしょう。

英語を勉強しよう

海外カンファレンスですから、スピーカーは全員英語で発表します。行くからには内容をなるべく理解したいので、英語も改めて勉強することにしました。

TOEIC Listening & Reading

まずはリスニング。TOEICは昨年受けたのですが、リスニングは全然聞き取れた気がしなかったので、改めて受験することにしました。

やったことは、去年買った単語集を改めてCDを聞きながら覚え直し、

CD-ROM付 改訂版 TOEIC(R)TEST 英単語 出るとこだけ!

新形式の問題集をやり直したことです。

公式 TOEIC Listening & Reading トレーニング リスニング編

公式 TOEIC Listening & Reading トレーニング リーディング編

公式TOEIC Listening & Reading 問題集2

おかげでだいぶ問題を聞き取れるようになり、スコアもかなり上がりました(リスニング390 -> 455)。

リーディングはちょっと下がっちゃったけど・・・😅(リーディング435 -> 405)

マンツーマン英会話

次にスピーキング。これを機会に、ガチで勉強することにしました。

会話力を上げるならマンツーマン英会話だろう、ということで、BerlitzとGabaを体験して前者に通うことにしました。

まあ、どちらも安くはないですね。。。ただ、ずっと外国人講師の方を相手に会話することを自分に強制できるので、とてもいい勉強になりました。

実際に向こうに行かないと分かりませんが、以前に比べれば話したいことがスッと出るようになった気がします。

いざ出発!

さて、明日夜の便で出発です!行ってきまーす!

「Spring Bootの本当の理解ポイント」というタイトルでJJUG CCC 2017 Fallに登壇してきました! #jjug_ccc

登壇してきた

www.slideshare.net

今回で8回連続8回目の登壇になりました。今後も記録を伸ばしていきたいです。

スライドもかなり多くの方々に読んでいただけているようで、現時点で4100viewsを超えています。

アンケートの結果はこちら。ご満足いただけたようで何より。

f:id:MasatoshiTada:20171123090142p:plain

f:id:MasatoshiTada:20171123090205p:plain

何故この内容にしたか

最近、Java EE研修のお客様はめっきり減り😅、ほぼSpringの研修ばかりやっています。

その中で実感しているのは、Spring BootでSpringに初めて触れたという方が多いと言うことです。

Spring BootはSpringの面倒な部分を隠蔽するので、一見便利なのですが、その面倒な部分を理解していないと、カスタマイズやトラブルシュートが出来ません。

また、世の中の「Spring Bootのブログ」を読んでいると「ん?コレ、BootじゃなくてSpringの話だよね?」と思う記事が非常に多いです。

そういった世の中に警鐘を鳴らしたいと思って、今回のテーマを選びました。

やってどうだったか

前述のとおり8回出させていただきましたが、ようやっと自分の立ち位置というか、自分に求められているものが分かってきたような気がします。

僕は現役のエンジニアではありません。そんな自分が、CCCに参加されるエンジニアの皆さんに何を提供できるのか、迷った時期もありました。

僕には、「現場で使ってこうだったよ!」とか「こんなテクニックがあるよ!」という話はできないからです。

しかし今回のように、中・上級者の方は分かっているけど暗黙知になってしまっている事柄を、整理して分かりやすく伝える、そのための資料を作る、ということなら僕にも出来ます。

初・中級者の方にとっては知らなかったことを知っていただけるし、中・上級者の方には人に説明するための資料や説明手順を提供できます。

次回以降もこんなテーマを見つけてやっていきたいと思います。

この4年間をちょっと振り返る

僕の初参加、そして初登壇は2014 Springでした。

正直、その頃はどのセッションを聞いてもサッパリ分かりませんでした。でも、3回目くらいから、ようやっと徐々に分かるようになってきました。

何年も前の内容が、今になって役に立つことも結構よくあります。例えば、今はMicroservices関連のこともやっているのですが、@nabedgeさんの12 Factorの資料は今になってとても参考になっています。キーワードだけでも知っているといないではだいぶ違います。

www.slideshare.net

登壇することで、自分の技術力がかなり上がったようにも感じています。資料作りのときにメッチャ調べなきゃいけないし、それは結構しんどい工程でもあります。しかし、それを何回も繰り返すことで、自分の知識が整理されたり、知らなかったことを知れたりして、自分の技術力がどんどん上がっていきます。

あと何より、色んな人との繋がりができたのが大きいですね。発表者になることで、声をかけてもらう回数が増えました。自己紹介が手短で済む(「今日○○で登壇した多田と申します」とか)ので、こちらから他の登壇者の方にお声がけするのも飛躍的に楽になります。懇親会でボッチにならずに楽しめるのは本当に嬉しい!w

ブログやTwitterでしか知らなかった方や、登壇者として「この人すごいなー!」としか思えなかった雲上の人ともお話できて、更に色んな技術情報が入ってきます。そこで、自分の理解が合っていたことを再確認できたり、間違っていたら修正できたり、全く知らなかったことを知れたりします。

自分にとって、JJUG CCCは無くてはならないものになりつつあります。JJUG CCCが無かったら、今の自分は無いですね。

全体的な感想

前回あった会場混雑の問題が、一方通行の導入や分かりやすいサインなどで解消されていました!参加者数も絞ったのかな?また、今回は参加できなかったのですが、アンカンファレンスなど新しい取り組みもありました。

費用面や運営面など、規模の拡大はこれ以上はなかなか難しいでしょうね・・・。

イチ参加者として、個人的にはこれくらいの規模が上限かな・・・と思います。これ以上規模が大きくなると、同時並行のセッション数が多くなるので、聴き逃してしまうセッションが多くなってしまうからです。

JJUG幹事の皆さん、ボランティアスタッフの皆さん、いつもありがとうございます。

僕もスピーカーとして、CCCへのコントリビュートを続けていきたいと思います!

JJUG CCC 2017 Springにスポンサー枠で出てきました! #jjug_ccc

今回もJJUG CCCに登壇しました。

これまでは普通にCfPに応募していたのですが、今回は会社とも相談して、初めてスポンサー枠として登壇しました!

今回で連続出場記録は7回連続7回。今後も記録を伸ばしていきたいです。

今回の登壇テーマ

いろいろ考えたんですが、最近はRESTやMicroservices関連の業務も多いので、その文脈で出てくることの多いセキュリティプロコトル「OAuth 2.0」をテーマとしました。

OAuth 2.0対応のライブラリ「Apache Oltu」+Payara MicroProfileを利用してサンプルアプリを作り、OAuth 2.0の仕様・HTTPS通信の様子・Javaプログラムのサンプルをご紹介しました。

120名部屋が満員で、後ろに追加の椅子も出されたくらい大盛況でした!聞いてくださった皆様、本当にありがとうございます。

アンケート評価はこちらです。

f:id:MasatoshiTada:20170522201617p:plain

f:id:MasatoshiTada:20170522201625p:plain

「難しい」という評価が多数派でした。OAuth 2.0そのものが難易度高めなのですが、それを如何に分かりやすく伝えるかがトレーナーの腕の見せどころなので、反省しきりです。

スライドの順番とか、もっと図を多めにするとか、Javaコードを省いてHTTPSでのやり取りにフォーカスするとか、改善の余地はあると思います。

新人研修時期が落ち着いたら社内勉強会でも発表したいので、そのときに修正しようかなあ・・・。

togetterまとめはこちら。

togetter.com

後半のほうに書いてありますが、緊張のあまりデモの準備を忘れてしまい、最後にやったはいいが動かなかったという・・・(^^;

これも反省です。

聴講したセッション

非機能要件とSpring Boot (@garbagetownさん)

IPAが公開している非機能要求グレード(http://www.ipa.go.jp/sec/softwareengineering/reports/20100416.html)の項目のうち、Spring Boot Actuator・Spring Security・Spring Data JPAなどでどのような項目をどのようにしてカバーできるかという解説でした。

研修やってるだけだと、あまり意識しないこともあるので、とても参考になりました。

Java EE 8 and its latest update (David Delabasseeさん)

Spring業務が多くなったとは言えJava EE屋さんでもあるので、このセッションは外せませんでした。

CDI 2.0・JAX-RS 2.1・Servlet 4.0など、新機能の解説が主でした。EE 9の話は特に無し。2018年にJava EE 8対応のWebLogic Serverが出る予定と言うのが、1番のサプライズでした。

今年7月にJava EE 8、出るのかなあ・・・。

How to use MicroProfile and a way to rebirth Japanese enterprise computing (@khasunumaさん)

我らがGlassFishユーザー会会長・蓮沼さんのセッションです。内容は主にMicroProfile(http://microprofile.io)でした。数少ないJava EE関連のセッションです(僕も頑張ってJava EEでサンプル作りました)。

最後に質問タイムがあったので、僕は「MicroProfileとJava EEの統合はあり得ると思いますか?」と質問しました。

MicroProfileでは、設立当初から「Oracleが友好的であれば歓迎する」(表現違ったかも?)というスタンスだそうです。

さて、これもどうなることやら・・・。

ヤフーの広告レポートシステムをSpring Cloud Stream化するまで (Yahoo! Japanさん)

広告レポートシステムのアーキテクチャを、PHPPerl→Spring Batch→Spring Cloud Streamという歴史的流れを追って解説していました。

DBからポーリングするのではなく、イベント駆動にすることで負荷を減らしパフォーマンスを向上させたのが興味深かったです。

Java x Arduinoで始めるIoT / フィジカルコンピューティング (@yusukeさん)

正直、自分のセッションで力尽きてて、あまり記憶がありません・・・(^^;

JavaプログラムでLEDが光ったりタイミングを変更できたりTwitter4J使ってツイートできたり、とても面白かったのを覚えています。

感想など

ついに参加者が1000人を超えたとのこと。すごすぎるなあ・・・。

今回はセッションが45分間になり、その分だけ休憩時間が15分になりました。なので、休憩中のお手洗いなどは余裕ができました。

人数が多くなった分、通路が混雑したりということはありましたが、通路を一方通行にしたり、部屋によって休憩時間を少しズラしたりすることで、ある程度解決できるのではと思います(アンケートに記入済み)。

会場の変更という手もあるかとは思いますが、更に広い会場となると値段も高くなる(現状で既に数百万円レベル)し、運営も難しくなるでしょう。うーむ、難しいですね・・・

運営側でない自分ができる貢献は、登壇やスポンサー枠の他、アンケートやブログなどでの問題点の発見と解決策の提案かなあ、と思っていますし、非力ながらも貢献を続けたいと思っています。

幹事およびボランティアスタッフの皆様、いつもありがとうございます。お疲れ様でした!

読書録:ロッシェル・カップ「日本企業の社員は、なぜこんなにもモチベーションが低いのか?」

きっかけ

www.cm-publishing.co.jp

この本というかロッシェル・カップさんを知ったのは、Twitterマイクロソフト牛尾さんをフォローしていて、牛尾さんが色んな所でロッシェルさんと発表されたのを見たのがきっかけだった。

いちおう中小企業診断士という資格を持っていて(さいきん休業したけど)、経営・組織・マネジメントなどにも興味がある分野なので、この本を読んでみることにした。新しいビジネス書を読むのは久しぶりだった。

最初はKindleの試し読みで第1章だけ読んだのだが、とても良かったので図書館で借りることにした。

読んでいて辛い

これが率直な感想。なぜ辛いのかというと、これまでの9年間の社会人生活の中でずっと思ってきたことが網羅されていて、共感できることがあまりに多すぎたからだ。どこがどう共感できたかは、差し障りがありそうなのでここには書かない。

年功序列は若年層にも中高年にもモチベーションの低下を招く

あまり給与のことは多くは書かれていないので、ここは自分が思ったこと。

年功序列だと、若年層はどれだけ頑張っても給料が上がらないから、モチベーションが低下する。

しかし実は、年功序列は中高年の方のためにも、良くないのではないか。完全に想像だけど。年功序列っていうけど、どこかの年齢(たぶん40代くらい)で昇給は頭打ちになるから。

年齢にかかわらず、頑張り具合で給料が大幅で上下したほうが、モチベーションがキープできるんじゃないかな。

企業のマネジメントだけの問題ではない

最後の第8章のみ、企業向けのメッセージではなく、社員向けのメッセージになっている。

この本では、主に日本企業のマネジメントに対する提言が書かれているが、われわれ社員側の意識も大切だと思う。

マネジメント層だって人間だ。完璧ではない。社員も多様だし、全員が満足して働ける会社なんてなかなか作れない。

会社への不平不満をただ並べるばかりでは、何も改善しない。会社のためにも自分のためにもならない。そんな暇があったら、いま自分ができることを精一杯やることが大事なんじゃないか。

自分で自分の目標を立て、自分のスキルや経験を磨く術を考える。業後の勉強でもいいし、業務でも興味がある仕事に積極的に手を挙げる。

勉強会に出てみたり、ブログ書いてみたり、勉強会に登壇してみたり、そこで色んな人と交流してみたり。ITってこれがやりやすいからいい。

これって、自分自身のためにもなるし、自分のスキルが上がれば、それが結果的に会社のためにもなるんじゃないかな。少なくとも、この30半ばのおっさんはそう思って日々仕事してます。

この第8章からは、とても勇気をもらった。

まとめ

久々に、いいビジネス書を読んだ。ビジネス書から勇気をもらったのは、ドラッカー以外でははじめてだ。

働く人、マネジメントの人、すべての人におすすめしたい。

マイクロサービスアーキテクチャ読書会 資料一覧 #MSA読書会

第1章 : マイクロサービス

(未発見)

第2章 : 進化的アーキテクト

(未発見)

第3章 : サービスのモデル化方法

Msa reading chap3

第4章 : 統合

前半

Msa読書会#3前半

後半

第三回マイクロサービスアーキテクチャ読書会(後半)

第5章 : モノリスの分割

モノリスの分割 // Speaker Deck

Payaraのバグを報告してみよう

この記事は?

Payara Advent Calendar 2016の22日目です。

Payaraは、GitHub上でオープンソースで開発されています。Payara開発チームは、誰でもGitHubのIssueに書き込んでバグ報告してくれてOK、というスタンスをとっています。

僕は過去2回*1バグを報告し、修正してもらうことができました。

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

Payara does NOT rollback when RuntimeException occurs in CDI @Transactional method using JDBC · Issue #505 · payara/Payara · GitHub *2 *3

今回は、修正してもらいやすいIssueの書き方について、僕が自分なりに気をつけていることをご紹介します。

Payaraの今使用しているバージョン・最新バージョン・過去のバージョンで比較する

今お使いのPayaraは最新バージョンでしょうか?もしそうでなければ、まずはPayaraの最新バージョンで動かしてみましょう。

Payaraは四半期に1回リリースされており、積極的にバグ修正や機能追加が行われているため、もしかしたらバグが修正されているかもしれません。

もし、最新バージョンで動けばそれでいい*4のですが、もし最新バージョンにしても問題が解決しなかったり、そもそも今お使いのものが既に最新バージョンであれば、1つ前など過去のバージョンのPayaraで試してみましょう。

過去のバージョンで期待通りに動作すれば、最新バージョン特有のバグの可能性があります。もし、過去のバージョンでも動作しなければ、以前から潜在していたバグの可能性があります。

余談ですが、Payara公式サイトからのダウンロードは、時とネットワーク環境にもよるのですが、めっちゃ遅いことが多いです(--;

僕は、過去にダウンロードしたPayaraのZIPファイルは、なるべく保存しています。

バグが再現する最小限のサンプルアプリを作り、GitHubにアップする

Payara開発チームは主にイギリスの方々なので、言語は英語です。英語でIssueを書くのは結構大変です。僕もまだまだ素早く正確に書くことができません(^^;

しかし、我々の共通言語であるJava言語であれば、英語圏の方を相手にしても、意図は明確に伝えられるはずです。

なので、バグが再現するサンプルアプリを作りましょう。さらにそれをGitHubにアップすれば、すぐにPayara開発チームに確認してもらうことができます。

このサンプルアプリは、Payara開発チームにバグを伝えることが目的なので、不要な機能は作らず、本当にバグが再現するだけの最小限のアプリにしましょう。

また、この最小限サンプルを作っていくことで、自分の中でも「どこがバグっぽいんだろう?」ということが明確になり、よりIssueを書きやすくなります。

設定はスクリプト化して、サンプルアプリと一緒にGitHubにアップする

GlassFish/Payaraの設定は、asadminコマンドまたはブラウザで開ける管理コンソールで行います。

管理コンソールはGUIで設定できるので非常に便利な反面、同じ設定手順を再現するのに時間がかかったり、打ち間違いなどで設定をミスする可能性があります。

よって、単純な設定ミスだったのか、設定は合っていて本当にバグだったのかが、自分にもPayara開発チームにも分かりづらくなってしまいます。

なので、設定はできる限りasadminコマンドで行い、それを1つのシェルスクリプトにまとめましょう。それを最小限サンプルと一緒のプロジェクトにまとめてGitHubにアップしてしまえば*5、Payara開発チームにも使ってもらえます。

また、スクリプト化することで、Payaraの複数バージョンでの確認もやりやすくなります。PayaraのZIPを展開して、スクリプトを実行するだけで設定が完了しますので、時間もかからないし、設定をミスする心配が少なくなります。

asadminコマンドについては、蓮沼さんのブログうらがみさんのブログを確認しましょう!

発生する現象とその再現手順を明確にIssueに書く

さて、ここまで準備が完了すれば、あとは頑張ってIssueを書くだけです。

どうしてもここは英語がついて回りますが、「どんな現象が発生するのか?」「最小限アプリを使って、どのような手順でその現象を再現できるか?」の2点を外さなければ大丈夫です。

これをコードやスクリーンショットを交えて書けば、英語の文章も少なく済みます。もちろん、GitHubにアップしたサンプルアプルのURLも忘れずに、Issueの最初の方で書いておきましょう。

また、最近はIssueのテンプレートが作成されて、使っているPayara・OS・JDKのバージョンを書く欄がありますので、これも埋めてください。

Payaraに貢献しよう!

Payaraを利用している方、もし何かバグっぽいものに遭遇したら、ぜひIssueを書いてみてください。

それにより、PayaraそしてGlassFishがより良いものとなっていきます。そしてそれは、Java EE全体への貢献にもつながるはずです。

それでは、Enjoy Payara & Java EE!!

*1:本当は3回なんですが、僕の勘違いだったことが1回あったため、含めていません(^^;

*2:こちらのバグは、僕が発見したものではなく、僕はIssueを書いただけです。詳細はこちらのコメント欄へ

*3:しかし、「Payara does not rollback」は我ながらカッコ悪いですね・・・。正しくは「Rollback does not work」とかでしょうか?

*4:よくない場合もあると思いますが

*5:パスワードなどの機密情報はなるべく伏せてアップしてください

ThymeleafでApache Shiroを使うためのライブラリを作ってみた

この記事は?

Java EE Advent Calendar 2016の19日目です。

昨日の記事は@khasunumaさんの「MicroProfile によるアプリケーション開発」でした。明日は@emaggameさんです。

今回は、以前@n_agetsuさんが紹介されていたApache Shiroというセキュリティフレームワークを、Thymeleafで使ってみました。

Apache Shiro を使ってみました - 見習いプログラミング日記

上妻さんのGitHubのサンプルをforkして、ビューをJSPからThymeleafに書き換えました。

合わせて、ThymeleafでApache Shiroを使うためのライブラリを作ってみました。

ソースコードGitHubにあります。

  • ThymeleafのApache Shiro用Dialect

GitHub - MasatoshiTada/thymeleaf-extras-shiro

  • 上妻さんのサンプルをThymeleafに移植したもの

GitHub - MasatoshiTada/ShiroSample: Apache Shiro 1.2 JDBC Realm sample

Dialectとは

上妻さんのブログでも紹介されている通り、Apache ShiroにはJSPカスタムタグが用意されています(タグの一覧はこちら)。

今回は、これらのJSPカスタムタグと似たようなものを、Thymeleafで作成しました。

Thymeleafといえば、タグの中にth:で始まる属性を記述していくのが特徴です。これらの属性は、自作することも可能です。

作成できるものは、属性の他、タグやユーティリティオブジェクト*1などです。

Thymeleafでは、これらを1つにまとめて「Dialect」と呼ばれます。

Dialectを自作する方法は、Thymeleaf公式Webサイトに公開されています。

Extending Thymeleaf

条件によって表示/非表示を切り替える属性を作る

Apache ShiroのJSPカスタムタグは、認証済み/認証済みでない等の条件によって、タグで挟んだ部分を表示する/しないを切り替えるものがほとんどです。

<%-- このタグで挟まれた部分はログイン中のみ表示される --%>
<shiro:authenticated>
    <h3>Hello, <shiro:principal/></h3>
    <form action="/ShiroSample/users/logout" method="post">
        <div class="form-group">
            <button type="submit" id="logout-button" class="btn btn-primary">logout</button>
        </div>
    </form>
</shiro:authenticated>

この<shiro:authenticated>タグを、Thymeleafの属性で下記のように作り変えてみました。(<shiro:principal>タグについては後述します)

  • Thymeleafの場合
<!--/* この属性で挟まれた部分はログイン中のみ表示される */-->
<div shiro:authenticated>
    <h3>Hello, <span shiro:principal>foo@sample.com</span></h3>
    <form th:action="@{/users/logout}"
          action="./index.html" method="post">
        <div class="form-group">
            <button type="submit" id="logout-button" class="btn btn-primary">logout</button>
        </div>
    </form>
</div>

shiro:authenticated属性は、今回僕が作成したものです。機能はJSPカスタムタグと同じです。

条件によって表示する/しないを切り替える属性は、ThymeleafのAbstractStandardConditionalVisibilityTagProcessorクラスを継承すれば、簡単に作成できます。

このクラスにisVisible()という抽象メソッドが用意されているので、これをオーバーライドするだけです。

  • 全属性の親クラス
public abstract class AbstractSecureAttributeProcessor extends AbstractStandardConditionalVisibilityTagProcessor {

    public static final int ATTR_PRECEDENCE = 300;

    public AbstractSecureAttributeProcessor(String dialectPrefix, String attrName) {
        super(TemplateMode.HTML, dialectPrefix, attrName, ATTR_PRECEDENCE);
    }

    protected final Subject getSubject() {
        return SecurityUtils.getSubject();
    }

    protected abstract boolean isVisible(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, String attributeValue);
}
  • 認証済みの時のみ表示する属性
public class AuthenticatedAttributeProcessor extends AbstractSecureAttributeProcessor {

    /** HTMLで指定する属性名 */
    private static final String ATTR_NAME = "authenticated";

    public AuthenticatedAttributeProcessor(String dialectPrefix) {
        super(dialectPrefix, ATTR_NAME);
    }

    /**
     * 認証済みであればtrue
     */
    @Override
    protected boolean isVisible(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, String attributeValue) {
        return getSubject() != null && getSubject().isAuthenticated();
    }

}

親クラスを作っているのは、ShiroのJSPカスタムタグのソースコードを参考にしました。

AbstractStandardConditionalVisibilityTagProcessorクラスは、前述のドキュメントには書かれていないのですが、Spring Security用のThymeleaf拡張のソースコードを読んで知りました。

他にも、ロールの有無やパーミッションの有無で表示/非表示を切り替える属性を作ったのですが、すべて同じ作り方です。詳細はGitHubをご覧ください。

値を表示する属性を作る

さて、ShiroのJSPカスタムタグの中で、1つだけ役割が違うのが<shiro:principal>タグです。これは、ログイン中のユーザー名を表示するタグです。

<h3>Hello, <shiro:principal/></h3>
  • Thymeleafの場合
<h3>Hello, <span shiro:principal>foo@sample.com</span></h3>

値を表示する属性は、 ThymeleafのAbstractAttributeTagProcessorクラスを継承し、doProcess()メソッドをオーバーライドして作成します。

public class PrincipalAttributeProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTR_NAME = "principal";

    public PrincipalAttributeProcessor(String dialectPrefix) {
        super(TemplateMode.HTML, dialectPrefix, null, false, ATTR_NAME, true, 1000, true);
    }

    /**
     * 認証済みであればtrue
     */
    protected boolean isAuthenticated() {
        return getSubject() != null && getSubject().isAuthenticated();
    }

    @Override
    protected void doProcess(ITemplateContext context,
                             IProcessableElementTag tag,
                             AttributeName attributeName,
                             String attributeValue,
                             IElementTagStructureHandler structureHandler) {
        String name = isAuthenticated()
                ? getSubject().getPrincipal().toString()  // ログイン済みならばユーザー名
                : "GUEST!!!";  // ログイン済みでなければ"GUEST!!!"を代替のユーザー名とする
        
        // 値を出力する
        structureHandler.setBody(HtmlEscape.escapeHtml5(name), false);
    }
}

ちょっと長いですが、値を出力しているのは、最後の行のstructureHandler.setBody()です。

作った属性をまとめて、1つのDialectを定義する

public class ShiroDialect extends AbstractProcessorDialect {

    private static final String DIALECT_NAME = "Shiro Dialect";

    public ShiroDialect() {
        super(DIALECT_NAME, "shiro", StandardDialect.PROCESSOR_PRECEDENCE);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<>();
        processors.add(new GuestAttributeProcessor(dialectPrefix));
        processors.add(new NotAuthenticatedAttributeProcessor(dialectPrefix));
        processors.add(new UserAttributeProcessor(dialectPrefix));
        processors.add(new AuthenticatedAttributeProcessor(dialectPrefix));
        processors.add(new HasRoleAttributeProcessor(dialectPrefix));
        processors.add(new LacksRoleAttributeProcessor(dialectPrefix));
        processors.add(new PrincipalAttributeProcessor(dialectPrefix));
        processors.add(new HasAnyRoleAttributeProcessor(dialectPrefix));
        processors.add(new HasPermissionAttributeProcessor(dialectPrefix));
        processors.add(new LacksPermissionAttributeProcessor(dialectPrefix));
        return processors;
    }

}

AbstractProcessorDialectクラスを継承して、getProcessors()メソッドをオーバーライドします。

このメソッドの中で、作成した属性を表すクラスをインスタンス化して、すべてSetに格納して返します。

IntelliJEclipseで属性の補完ができるようにする

この作業は必須ではないのですが、やっておくと便利です。

src/main/resources配下*2に、下記のようなXMLを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<dialect xmlns="http://www.thymeleaf.org/extras/dialect"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.thymeleaf.org/extras/dialect http://www.thymeleaf.org/xsd/thymeleaf-extras-dialect-2.1.xsd"
         prefix="shiro"
         namespace-uri="http://suke_masa.com/thymeleaf/shiro"
         namespace-strict="false"
         class="com.suke_masa.thymeleaf.extras.shiro.dialect.ShiroDialect">

    <attribute-processor
            name="authentication"
            class="com.suke_masa.thymeleaf.extras.shiro.dialect.processor.AuthenticatedAttributeProcessor">
        <documentation
                reference="authentication attribute"/>
    </attribute-processor>

    <attribute-processor
            name="principal"
            class="com.suke_masa.thymeleaf.extras.shiro.dialect.processor.PrincipalAttributeProcessor">
        <documentation
                reference="principal attribute"/>
    </attribute-processor>

    <!-- 一部省略 -->
</dialect>

これを作っておくと、このDialectを利用するときに、IntelliJEclipse(Thymeleafプラグイン必須)で属性の補完が効くようになります。

f:id:MasatoshiTada:20161219105028p:plain

作成したDialectを使う

TemplateEngineクラスのaddDialect()メソッドで、作成したDialectを追加します。

@Provider
public class ThymeleafTemplateProcessor extends AbstractTemplateProcessor<String> {

    @Context
    private HttpServletRequest httpServletRequest;
    @Context
    private HttpServletResponse httpServletResponse;

    private TemplateEngine templateEngine;

    @Inject
    public ThymeleafTemplateProcessor(Configuration config, ServletContext servletContext) {
        super(config, servletContext, "html", "html");
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
        templateResolver.setPrefix((String) config.getProperty(MvcFeature.TEMPLATE_BASE_PATH));
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateEngine = new TemplateEngine();

        // 作成したDialectを追加する
        templateEngine.addDialect(new ShiroDialect());
        templateEngine.setTemplateResolver(templateResolver);
    }

    // 以下省略

まとめ

  • AbstractStandardConditionalVisibilityTagProcessorを継承して、条件で表示/非表示を切り替える属性を作る
  • AbstractAttributeTagProcessorを継承して、値を出力する属性を作る
  • AbstractProcessorDialectを継承してDialectを作成し、作った属性を1つにまとめる
  • XMLを作成して、IDEで補完が効くようにする
  • TemplateEngineクラスのaddDialect()メソッドで、作成したDialectを追加する

参考資料

Apache Shiroのミニブック

Apache Shiroカスタムタグのソースコード

thymeleaf-extras-springsecurityのソースコード