Challenge Engineer Life !

エンジニア人生を楽しみたい!仕事や趣味で学んだ技術的なことを書いていくブログです。

Azure DevOpsでJavaプロジェクトを作成する

この記事は「Microsoft Azure Advent Calendar 2018」19日目のエントリです。

先日JJUG CCC(Japan Java User Group Cross Community Conference)に参加した際に、てらだよしおさん(@yoshioterada)のセッションで「Azure DevOpsを使うとポチポチとクリックするだけでJavaアプリの開発/CI環境などができる」と聞いて「えーホントに?」と思って試したところ、ホントでした。

azure.microsoft.com

どんな感じなのか、ポチポチした画面をキャプチャしたので紹介します。

まずはAzureのダッシュボードにある「すべてのサービス」を選択します。 f:id:kikutaro777:20181219002406p:plain

「devops」でフィルタすると「DevOps Project」が表示されるので選択します。 f:id:kikutaro777:20181219002416p:plain

「DevOps projectの作成」ボタンを押します。 f:id:kikutaro777:20181219002446p:plain

「Java」を選択います。(メニューには「C」もあるんですねー) f:id:kikutaro777:20181219002636p:plain

男は黙って「JSF」 f:id:kikutaro777:20181219002642p:plain

Serviceは今回「Windows Web App」を選びました。 f:id:kikutaro777:20181219002650p:plain

各種名前を入れて環境を作ります。Azure DevOpsの環境ではOrganizationという単位が一番上位にあって、その中にProjectを作るイメージのようです。「DevOps Organization」のメニューも別途あるのですが、ここでまとめて作成ができるようになっています。 f:id:kikutaro777:20181219002727p:plain

しばらく待てば、できあがり!これだけです。 f:id:kikutaro777:20181219002750p:plain

下図のCI/CD pipelineパネルをみるとわかりますが、Gitのレポジトリとそれに連動するCIが作成されています。「Project homepage」を押すと作成されたAzure DevOps環境の画面が表示されます。 f:id:kikutaro777:20181219075628p:plain

こんな画面です。 f:id:kikutaro777:20181219012421p:plain

デプロイされたWebページ(2つ上の図のApplication endpoint)にアクセスすると、こんなページが表示されます。 f:id:kikutaro777:20181219002843p:plain

これほんとにJSFなの…?とGitのソースをみてみるとindex.xhtmlがあってFaceletsタグが確認できます。 f:id:kikutaro777:20181219002923p:plain

 <h:outputText value = "#{helloBean.message}" />

バインドされてる管理BeanのHelloBean.javaはCDI管理対象BeanではなくJSF管理対象Beanでしたー…。まぁ単なるデフォルトのサンプルコードなので参考レベルですね。 f:id:kikutaro777:20181219003005p:plain

ホントにポチポチするだけでこんな環境が揃うのかとちょっとびっくりしました。Projectのダッシュボードは自分で色々カスタムできるようになっていて、ウィジェットをドラッグ&ドロップで並べるだけです。便利そう。 f:id:kikutaro777:20181219013002p:plain

メニューにあるArtifactsではMaven(他にnpm、NuGetなど)のパッケージリポジトリもできるらしいです。

pipelineのソース管理はGitHubなどにも切替できるので、自由度は高いですね。 f:id:kikutaro777:20181219080712p:plain

プランみると個人開発とか小規模であればFreeライドできてしまうようなので、実際に使ってみようと思います! f:id:kikutaro777:20181219080858p:plain

IBM Cloud ライト・アカウント&Java EEアプリのデプロイ #IBMCloud #jjug_ccc

先日参加したJJUG CCCのブースLTで「IBM Cloudライト・アカウント」の存在を知りました。その後、ブースに行って @Santea3173 さん とお話して興味が湧いたので早速試してみました。

IBM Cloud ライト・アカウント

クレカ登録なしで無期限に利用できる&IBM Websphere Liberty Profileが動く、ということでJava EE(Jakarta EE)のサンプルを試すのに良さそうなクラウド環境です。

アカウントの作成は簡単です。手順が気になる方は以下を参考にしてください。
https://qiita.com/ayatokura/items/42d4bc728112c733c80d

ライト・アカウントに関する概要を知りたい方は以下がおススメです。
www.project-respite.com

Java EEアプリのデプロイ

アカウントを取得してログインするとDashboardが表示されるので「リソースの作成」を押します。
f:id:kikutaro777:20180602130524p:plain

今回はJava EEアプリを試すため「Liberty for Java」を選びました。その他の言語含めてライト・アカウントでも色々遊べそうです。
f:id:kikutaro777:20180602130650p:plain

アプリ名やホスト名を入れます。
f:id:kikutaro777:20180602141841p:plain

中はCloud Foundryが動いているので、Cloud Foundry CLIで簡単にデプロイなどができます。

自分の環境の話ですが、前にPivotal Web Servicesを触っていたので、接続先はPivotalになっていました。まずはIBM Cloudへ切り替えました。

cf api https://api.ng.bluemix.net

ログインします。

cf login

Java EEアプリはとりあえず超簡単なJSFページを作って試しました。ドキュメントをみるとmanifest.ymlが必要そうにみえましたが、なくても動きました。
github.com

なお、これを実装したときは手元のPayaraで動作確認をしてました。で、Websphereで確認することなく、そのままwarをIBM Cloudへデプロイしました。

cf push kikutaroibmcloudsample -p target\IbmCloudLightSample-0.0.1.war

f:id:kikutaro777:20180602133158p:plain

デプロイ後、アクセスすると以下のページが表示されます。
f:id:kikutaro777:20180602141925p:plain

名前を入れると、呼び返してくれる簡単なWebアプリですが、以下のようにちゃんと動きました。かなりお手軽ですね。
f:id:kikutaro777:20180602141928p:plain

管理Beanについて

ちなみに、最初は管理BeanをCDI(@Named)で作っていたのですが、実行時に以下のエラーとなりました。CDI管理Beanが認識されていないようにみえます。Payaraでは問題なかったので、何か設定が必要なのかも?

f:id:kikutaro777:20180602142709p:plain

とりあえずJSF管理Bean(@ManagedBean)で実装したら動いたのでソースは@Managedを使ったものとなっています。この辺りについてはどこかで原因を探ります。

ログをみて気付いたのですが、WebsphereのJSF実装はmyfacesなんですね。

org.apache.myfaces.view.facelets.el.ContextAwarePropertyNotFoundException: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'indexBean' resolved to null
	at org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression.getType(ContextAwareTagValueExpression.java:79)
	at org.apache.myfaces.shared.renderkit._SharedRendererUtils.findUIOutputConverter(_SharedRendererUtils.java:78)
...

PrimeFacesのTimelineコンポーネントで1年を振り返ろう!

この記事は「Java EE Advent Calendar 2016」の8日目の記事です。
昨日は@sudoyuさんの「Spring系の2016年の現行プロジェクトをまとめてみる」でした!
明日は@megascusさんです!

自分にとってJava EE Advent Calendarは中々思い入れの強いイベントです。というのも、はじめてお誘い頂いた当時の自分は「アドベントかれんだー?なにそれ…おいしいの?」って感じでして、、

で、はじめて書いたAdventが

kikutaro777.hatenablog.com

です。2012年か…懐かしい。

atnd.org

ということで、4年を経て、改めてPrimeFacesで書いてみます。

実はPrimeFacesは、今もまだしっかりと継続して機能拡張が続いています。バージョンは6.0で、これはOSS版として誰でも自由に使えます。もう1つelite版という有償版があるのですが、そちらは6.0.8となっています。

バージョン6.0が出たとき、いくつかコンポーネントが追加となりました。

PrimeFaces 6.0 Released |

といっても元々PrimeFaces Extensionにあったコンポが統合された形なのですが。6.0.8ではOrganigramなど新しいコンポが増えてたりして、まだまだ拡張されそうです。

ということで、6.0で増えたTimelineコンポーネントというのを使って、自分のこの1年の仕事を振り返ってみました。

f:id:kikutaro777:20161207225040j:plain

うん。色々と心が折れるに十分な1年でしたね。

ソースはこちらにあるので、是非皆さんも1年をJava EEのコードで振り返ってみてください!

github.com

ちなみに、こんなカラフルにしたり

f:id:kikutaro777:20161208000649j:plain

左にある項目をドラッグ&ドロップで投げ込んだり

f:id:kikutaro777:20161208000713j:plain

上下のTimelineで連動させたり

f:id:kikutaro777:20161208000907j:plain

意外と多機能なコンポーネントとなっています!!

Java EE Advent Calendar 2016の8日目が空いてしまってたので、急きょ入れたため、非常に雑なネタとなってしまいました。すみません…。

軽量でモダンなコンポーネントライブラリ ButterFaces

少し前にJSFのSpec LeadでもあるEd Burnsさんが以下つぶやいていました。

JSFって下火な面もあって(盛り上がったことないだろ、というツッコミは置いておいて…)、さらにリッチコンポーネントライブラリに関しては、PrimeFacesが一人勝ちという状態なので、新しいコンポーネントライブラリが出てたことにびっくりしました。

www.butterfaces.org

2014年くらいからあるっぽいです。

デザイン的にはTwitter Bootstrapをベースとして、jQueryやHTML5の利点を取り入れたライブラリのようです。

ということで、少し触ってみました。

PrimeFacesになさそうなコンポをいくつか使ってみました。

f:id:kikutaro777:20150603220244g:plain

ビューは次のような定義です。

<h:form>
    <b:text label="ラベルー" value="#{butterFacesBean.value}" placeholder="hoge" />
    <b:commandLink value="送信" styleClass="btn btn-primary" action="#{butterFacesBean.commandLinkBtnAction()}"/>
    <b:tags value="#{butterFacesBean.tagsValue}" />

    <b:modalPanel id="mdl" cancelButtonText="cancel">
        <b:text value="hoge" />
    </b:modalPanel>

    <span onclick="butter.modal.open('mdl')">dialog</span>

    <b:textArea value="#{butterFacesBean.textArea}" expandable="true" />
</h:form>

前々からPinterestの検索テキストみたいなの欲しかったんで、tagsコンポはいいなーと。
textAreaもexpandableみたいな属性があって面白いです。

依存性は1つでmavenのpom.xmlでは次の定義をするだけです。

<dependency>
    <groupId>de.larmic.butterfaces</groupId>
    <artifactId>components-mojarra</artifactId>
    <version>1.8.1</version>
</dependency>

サイトのほうみるとcomponentsというartifactId(下の定義)が先に紹介されてますが、これだけだとテキストやボタンといった基本コンポーネントが使えませんでした。

<dependency>
    <groupId>de.larmic.butterfaces</groupId>
    <artifactId>components</artifactId>
    <version>1.8.1</version>
</dependency>

よくみるとコンポーネント紹介のところに「only mojarra version」とあります。

f:id:kikutaro777:20150603214759j:plain

基本コンポが使えないので、components-mojarraバージョンを使うのが普通じゃないのかな…と。

あと、HTML5はパススルーじゃなくて、普通に属性として定義されていました。

Showcaseでは属性を変えて色々試せるので、何ができるか、PrimeFacesのShowcaseよりちょっと良いかも。

f:id:kikutaro777:20150603221616g:plain

あ、ちなみにタイトルはサイトの英語から持ってきていますので、軽量でモダンかどうかは実際もっと使ってみないとホントかわからんです。

JSFのURLをきれいにするPrettyFaces

GW中、久しぶりにCore JavaServer Facesの本を改めて読み返していたのですが、RESTful Navigation and Bookmarkable URLsの章で「pretty URLs」という表現があることに気づきました。

かわいいurlって何?と思ったのですが、Alcで調べると、prettyには「きれいにする」という意味があるようです。eow.alc.co.jp

本の文脈的にもこれっぽい。。。で、本の中には

Currently, JSF does not have a standard mechanism for producing or consuming "pretty URLs", but since JSF 2.0, there is support for GET requests.

と記述があって、Bookmarkableな仕組みの話につないでいました。

で、そういえばPrettyFacesというライブラリがあったなぁと。

PrettyFaces

前にPrettyFacesを知ったときは、PrimeFacesやIceFacesのようなリッチコンポ―ネントの1つなのかなー?ぐらいの感じでスルーしてました(^^;; が、上記の意味を知って、改めてみてみたらJSFにおけるURLを色々と使いやすくするものでした。

f:id:kikutaro777:20150507223224j:plain

サイトTopページに

PrettyFaces is an OpenSource URL-rewriting library with enhanced support for JavaServer Faces – JSF 1.1, 1.2 and 2.0 – enabling creation of bookmark-able, pretty URLs. PrettyFaces solves the “RESTful URL” problem elegantly, including features such as: page-load actions, seamless integration with faces navigation, dynamic view-id assignment, managed parameter parsing, and configuration-free compatibility with other web frameworks.

とあります。JSFで"RESTful URL"…!?

気になったので、ちょっと触ってみました。

mavenのpom.xmlで以下追加しました。

<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-servlet</artifactId>
    <version>3.0.0.Alpha6</version>
</dependency>
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-config-prettyfaces</artifactId>
    <version>3.0.0.Alpha6</version>
</dependency>

WEB-INF配下に「pretty-config.xml」というxmlを追加します。

f:id:kikutaro777:20150507223528j:plain

今回はxmlを利用しましたが、アノテーションベースでも書けるようです(試してないです)

URLのマッピング

JSFでベタにWebアプリを作成すると、以下のようなURLになります(facesはservlet-mappingで変更可能+ここの例ではxhtmlを利用を前提としてます)

http://localhost:8080/PrettyFaces/faces/login.xhtml

PrettyFacesでは、これを

http://localhost:8080/PrettyFaces/login

と以下定義によってマッピングできます。

pretty-config.xmlの定義

<url-mapping id="login">
    <pattern value="/login" />
    <view-id value="/faces/login.xhtml" />
</url-mapping>

Path Parameters

PrettyFacesのDocumentationでは「3.2. Add Path-parameters to your mapped URL (REST)」のタイトルにあるようにRESTとして紹介しています。

http://localhost:8080/PrettyFaces/faces/product/order.xhtml

http://localhost:8080/PrettyFaces/product/ここにパラメータ

のようにマッピング。JSFのViewParamで取れるとのことで

<h:body>
    <h:form>
        <f:metadata>
            <f:viewParam name="id" value="#{orderBean.id}" />
        </f:metadata>

        <h:outputText value="#{orderBean.id}" />
    </h:form>
</h:body>

のように記述。

f:id:kikutaro777:20150507230638j:plain

サンプルそのまま書いてしまった(^^;けど動きました。ためしに「book」というパラメータにすると

f:id:kikutaro777:20150507230734j:plain

pretty-config.xmlは以下です。

<url-mapping id="order">
    <pattern value="/product/#{id}" />
    <view-id value="/faces/product/order.xhtml" />
</url-mapping>

なお、直接EL式でバインドも可能なようで、次のような設定でも動きました。

<url-mapping id="user">
   <pattern value="/#{userBean.user}" />
   <view-id value="/faces/user/user.xhtml" />
</url-mapping>

他にも色々と機能あるようなので、もう少しみてみようかなぁ。

JSFではServletやJAX-RSを比べて、URLを意識した設計をすることはあまりないと思いますが、その辺を結び付けたいときには使えるのかも。相性が良いかは置いておいて。

また、今回はお試しレベルにすぎず、実戦投入してるわけではないので、ご注意を(^^;

Java Day Tokyo 2015でJSFについてお話させて頂きました #javadaytokyo

昨日、有楽町の国際フォーラムで開催された「Java Day Tokyo 2015」に参加しました。

ブログをほじくりかえしてみたら、今年で3回目の参加(^^;時間の流れがはやいなぁ。kikutaro777.hatenablog.com
kikutaro777.hatenablog.com

今回のイベントでは、なんと…セッションでスピーカーを担当させて頂く機会を頂きました(^^;汗汗汗

今年はJavaが誕生して20周年という記念すべき年で、そんな年に自分なんぞが発表させて頂いていいものか、悩みもあったのですが…せっかくの機会ですし、前向きに考えて挑戦させて頂くことになりました。

発表は「Java EE 開発における JSF の活用について」でした。

普段仕事で使っているJSFに関して、(少し大げさですが)自分の持てる知識を全て出した感じです(^^;;

なお、資料に出てくるコードは全てではないですがGitHubのほうにもあげました。あまり準備時間がとれず、少し雑なコードだったりするのですが…今後少しずつ整備・追加していくかもしれません(特にスコープの所とか)github.com

Togetterもまとめて頂いてましたm(_ _)mtogetter.com

あとは感想などをつらつらと…。

発表準備

お話を受けたものの、開発の佳境やら何やら色々な仕事が重なってしまい……

調整能力が必要だなぁ、と痛感する日々でした。

後悔はしたくない、と出来る限りの時間を割きましたが、発表練習がほとんどできず、当日の喋りは微妙だったかと思います(-_-;少し悔しい感じです。

来週納品です。

他のセッション

基調講演の途中時間で接続確認やリハがあったので、基調講演をあまりちゃんと聞けませんでした(*´Д`)

その他のセッションも、自分の発表に向けた緊張がすごくて、スピーカー控室で練習してたり、ほとんど聞けずじまいでした(*´Д`)

自分の発表が終わったあとは気が楽になったので、楽天の岩崎さんのセッションを聞きにいきました。

岩崎さんのお話は力があって、いつも聞いていて楽しいです。私はStrutsや昔のJ2EEを全く知らないのですが、その辺りをよくご存じの上で比較しつつお話されるため、Java EEってホント進化してきてるんだなぁと改めて。

楽天さんがJSFのエキスパートグループに参加されてるのもすごいですね(^^)今後も色々と楽しみです。

おまつり

最後は大山さんと寺田さん司会の元、Javaのクイズ&LTがありました。

Javaはコミュニティが大事、ということで、名刺3枚を回りと交換という所から始まるという(^^;

めっちゃ楽しい時間でした。大山さんが楽しそうにJavaのあれこれを語るのは、みてるだけで楽しいですね。

そしてツッコミ所があるとすかさずツッコミを入れるJavaチャンピオンの櫻庭さん(^^;

こうした方々がずっと昔からJavaコミュニティを支えてて今に至るんだろうなぁー、20年ってすごいなぁと。

LTも1つ1つがとても印象的で良かったです。

勉強会やコミュニティに参加して、色々変わった、というのは私自身も同じなので、@syobochimさんやJava女子部の方、@zephiransasさん、@irofさんのお話は、それぞれとても共感するものでした。

懇親会

最後は東京駅近くのお店へ移動して、懇親会に参加させて頂きました。

ロジ子さんとお会いできて感動(^^)

Dukeケーキ!

f:id:kikutaro777:20150408212615j:plain

寺田さん櫻庭さんによるケーキ入刀でこんな姿にw

f:id:kikutaro777:20150408213503j:plain

大盛り上がりでした(^^)

じゃんけんでDukeキーホルダーゲットしました!そして、懇親会のお土産としてチロルチョコも!

f:id:kikutaro777:20150409141300j:plain

発表疲れもありましたが、吹っ飛ぶくらいに楽しかったです(^^)

これからもがんばろー。

レスポンスをgzip圧縮する

今の案件では海外からのアクセスもあるとのことで、通信のgzip圧縮入れた検証とかしようと思ってるのですが、そもそもどういう選択肢があるのか意外と知らなかったり…。

多分一番簡単なのは、フロント側のWebサーバ(ApacheやNginx)で設定する方法なのだと思います。

うちではイントラでユーザ数があまり多くない場合、フロント立てずに直接アプリケーションサーバ利用もあるのですが、GlassFishについて調べたらCompressionの設定がありました。

f:id:kikutaro777:20150325194749j:plain

以下のCompressionをonかforceにして、Mime Typesも必要に応じてtext/javascriptなど追加したり、圧縮する最低ラインを設定できたりするようです。

f:id:kikutaro777:20150325194755j:plain

ちょっと試してみたのですが、なんかうまくできなかった…orz
Stack Overflowのこの辺りも似た感じなのかなぁと(ここの話はcacheだけ?)

stackoverflow.com

で、もう1つ海外で結構引っかかったのは先日ブログでも紹介したOmniFacesを利用するというものでした。

kikutaro777.hatenablog.com

GzipResponseFilterという便利フィルタが用意されています。

GzipResponseFilter - OmniFaces Showcase

設定はweb.xmlに以下を追加するだけでした。

<filter>
    <filter-name>gzipResponseFilter</filter-name>
    <filter-class>org.omnifaces.filter.GzipResponseFilter</filter-class>
    <init-param>
        <description>圧縮かける閾値のバイト。0から9999が指定可能で、デフォは500。</description>
        <param-name>threshold</param-name>
        <param-value>500</param-value>
    </init-param>
    <init-param>
        <description>MIME TYPEの指定</description>
        <param-name>mimetypes</param-name>
        <param-value>
            text/plain, text/html, text/xml, text/css, text/javascript, text/csv, text/rtf,
            application/xml, application/xhtml+xml, application/javascript, application/json
        </param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>gzipResponseFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

Filterの適用前後を比べてみると次のような感じに。

gzip圧縮なし
f:id:kikutaro777:20150325200025j:plain

gzip圧縮あり
f:id:kikutaro777:20150325200117j:plain

以下辺りを参考に確認できるサイトでもみてみました。

syncer.jp
web-marketing.zako.org


f:id:kikutaro777:20150325200130j:plain

f:id:kikutaro777:20150325200137j:plain

計測した画面はPrimeFacesのコンポーネントを結構多く使ってる画面だったのですが、結構圧縮されるものだなぁと。

実際の効果や、逆にオーバーヘッドとかも気になるけど、とりあえずこれでいこうかなぁ…。

PrimeFacesのAutoCompleteコンポーネント

今回の開発案件ではPrimeFacesのAutoCompleteコンポーネントを利用しています。

手軽にサクッと使えて便利なのと、Java 8使っていればStream APIでのフィルタも簡単にできて良い感じです。

例えば次のような管理Beanを作って

@Named(value = "autoCompleteBean")
@ViewScoped
public class AutoCompleteBean implements Serializable{
    
    @Getter @Setter
    private String selectedMember;
    
    public List<String> listNogizaka(String input){
        return Arrays.asList("MaiShiraishi", "NanaseNishino" ,"RinaIkoma",
                "MaiHukagawa", "NanamiHashimoto","ErikaIkuta");
    }
}

ビューでは、autoCompleteのcompleteMethod属性に対して、表示するリストを返すメソッドをバインドします。value属性には選択した値を格納する変数を指定します。

listNogizakaメソッドの引数inputには入力された文字列が都度入ってきます。

<h:body>
    <h:form prependId="false">
        <p:autoComplete 
            value="#{autoCompleteBean.selectedMember}"
            completeMethod="#{autoCompleteBean.listNogizaka}" />
    </h:form>
</h:body>

実行して、テキストに文字を入力すると次のように該当部分がボールドで表示されたリストがでます。

f:id:kikutaro777:20150317184838j:plain

該当するリストだけ出したい場合などはフィルタ処理を入れるだけ。

public List<String> listNogizaka(String input){
    return Arrays.asList("MaiShiraishi", "NanaseNishino" ,"RinaIkoma",
            "MaiHukagawa", "NanamiHashimoto")
            .stream()
            .filter(m -> m.contains(input))
            .collect(Collectors.toList());
}

f:id:kikutaro777:20150317185048j:plain

Stream APIの練習にもいいですね。

forceSelection属性

候補リストからしか選択できない(リストにない文字列を入力しても無効化される)ようにしたい場合はforceSelectionをtrueにします。

<p:autoComplete forceSelection="true" ...
dropdown属性

何も入力していないときに、全ての項目を出したい場合などにはdropdown属性をtrueにすれば次のようにボタンがついて、クリックすると全ての選択肢が表示されます。

f:id:kikutaro777:20150317185728j:plain

cache属性

クライアント側で入力文字と結果のキャッシュもできるらしく、cache属性をtrueにすればいいようです。

cacheTimeout属性もあって、どれくらい保持するかも調整できるとのこと。

選択の反映

ビューにラベルを追加して、選択された項目を表示してみます。

<h:form prependId="false">
    <p:autoComplete 
        value="#{autoCompleteBean.selectedMember}"
        completeMethod="#{autoCompleteBean.listNogizaka}">
        <p:ajax event="itemSelect" update="txtMember" />
    </p:autoComplete>

    <p:separator />

    <h:outputText id="txtMember" value="選ばれたメンバは#{autoCompleteBean.selectedMember}" />
</h:form>

初期表示です。
f:id:kikutaro777:20150317190547j:plain

文字入力して、選択肢を選びます。
f:id:kikutaro777:20150317190602j:plain

選択結果が反映されます。
f:id:kikutaro777:20150317190617j:plain

ビューにあるajaxイベントが味噌で、これで選択時にサーバサイドへselectedMemberがセットされます。

<p:ajax event="itemSelect" update="txtMember" />

属性は他にもあって、基本的にはPrimeFacesのDocumentにちゃんと書いてあるので読んでみてください。

追記:注意書き

@shinsukeodaさんからコメント頂きました(^^;

今回の開発では選択候補が英数字のみのコードみたいなモノだったので、ちゃんと検証してませんでした。

上記サンプルで日本語を入れたレベルであれば普通に動きましたが、、、

f:id:kikutaro777:20150317194112j:plain

色々属性と絡めると駄目かもしれないので、注意が必要そうです。

PrimeFacesのようなJSFライブラリは、すごく便利な反面、ハマるとホント辛いときがあります。

Showcaseだけみて「おー、これ良さげ、採用」と上流工程で決め打ちされると、実際に使うときに「それできない…」みたいなこともありえます(実際何度か経験してます…)

なので、あまり普段利用していないコンポーネントの採用時には、しっかり検証することが大事かなと。

検証せずに採用を決めてしまうのは、実装する立場のことを考えていない人ではないかなと(^^;

JSFサポートライブラリのOmniFaces

JSFを利用していて、海外のStackOverflowをよく見る方はご存じだと思うのですが、回答の常連となっているBalusCさんというJSFマスターがいます。

で、この方が作られたOmniFacesというJSFのサポートライブラリがあるのですが、今回の開発で使ってみることにしました。

omnifaces.org

サイトの副題になっている「To make JSF life easier」って表現いいですね。JSFライフは辛いときもあるので…。

具体的にどんな機能が揃っているのか?は以下のshowcaseで確認できます。

OmniFaces Showcase

なお、OmniFacesの概要や誕生経緯とかについては以下翻訳でわかりやすくまとまっています。
www.infoq.com

ここにも書いてありますが、PrimeFacesやRichFaces等のJSFリッチコンポ―ネントライブラリとバッティングするようなものではなく、併用して使える支援ライブラリです。ライセンスはApache License 2.0。

OmniFaces自体はあまり大きいプロジェクトではなく、わりと個人寄りなライブラリなので、採用判断は選ぶ方におまかせ…です(^^;

で、どんなことができる?というところで、わかりやすい所を挙げてみます。

JSF使っているとFacesContextを使う機会が多いと思いますが、普通に書くと長いですよね(^^;

//nullチェック
if(FacesContext.getCurrentInstance() != null){            
    //web.xmlで定義したContextParamを取るとき
    String hoge = FacesContext.getCurrentInstance().getExternalContext().getInitParameter("hoge");

    //ProjectStage取得
    ProjectStage ps = FacesContext.getCurrentInstance().getApplication().getProjectStage();

    //先日のブログで書いたEL評価 http://kikutaro777.hatenablog.com/entry/2015/03/04/203233
    boolean beanChecked = FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{hogeBean.checked}", boolean.class);

    //その後の処理は略
}

自分でFacesContext用にWrapper/Helperクラスみたいなの作ってる人も多いかと思います。

上記と同じ処理がOmniFaces使うと以下のように書けます。

if(Faces.hasContext()){
    String hoge = Faces.getInitParameter("hoge");
    ProjectStage ps = Faces.getApplication().getProjectStage();
    boolean beanChecked = Faces.evaluateExpressionGet("#{hogeBean.checked}");

    //その後の処理は略
}

OmniFacesのFaces.javaのコードをみると

omnifaces/Faces.java at 2.0 · omnifaces/omnifaces · GitHub

各々ショートカット的に呼べるようにラップしてるだけなんですが。

あと内部的にFacesLocalというのを持っていて、毎回毎回getCurrentInstance()を呼ぶコストを下げているようです。

FacesLocal (OmniFaces 2.0-SNAPSHOT API)

Maven使っていれば以下定義をpom.xmlに追加するだけで使えます。

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.0</version>
</dependency>

FacesContextのevaluateExpressionGetなるメソッド

開発メモ。特殊なケースなので、あまり参考にせず、最終手段としての方法かなと。

JSFのFacesContextでは様々な情報を管理していますが、Applicationクラスが持つevaluateExpressionGet()なる黒魔術なメソッドを知りました。

https://javaserverfaces.java.net/docs/2.2/javadocs/javax/faces/application/Application.html

簡単にいうと、コード側からEL式を記述すると、そのEL式を評価して値を取ってくれます。

例えば、以下のようなコードです。

FacesContext context = FacesContext.getCurrentInstance();
boolean beanChecked =
    context.getApplication().evaluateExpressionGet(context, "#{actressBean.checked}", boolean.class);

画面に置いたチェックボックスをActressBeanという管理Beanのcheckedというboolean変数にバインドしているイメージです。

上記コードでは、その値をいつでも取れるという……なにこれ。

注意

これをあちこちで多用し出すと(ユニット)テストが死ぬので、なるべく使わないほうがいいと思います。

JSFはそもそもFacesContextを利用し出すと、モックで頑張るか、Arquillianなどを使うか、となっていくので、この辺を意識して上手く作らないとテストしやすいコードにならなかったり。(だから.NETもWebFormからMVCにいったのだし…)

以下は補足と備忘で、どんな状況でこれを使いたくなかったのか?書いておきます。

やろうとしたこと

元々やりたかったのは、PrimeFacesのajaxにおいて、EL式からバインドしてる呼出し先へパラメータを渡したいというものでした。

PrimeFacesではTreeコンポーネントのノードを選択したイベントを以下のようなajaxで書きます。

<p:tree var="act" value="#{actressBean.tree}" 
    selection="#{actressBean.selectedNode}" selectionMode="single">

    <!-- ノードの選択イベント -->
    <p:ajax event="select" listener="#{act.hello}" />
    
    <p:treeNode>
        <h:outputText value="#{act.name}" />
    </p:treeNode>
</p:tree>

ちなみにイベントにはselectの他にもツリーノードを展開したか閉じたか(expand/collapse)などもあります。
http://www.primefaces.org/showcase/ui/data/tree/events.xhtml

で、EL式で書いているactというのは、以下Actressクラスで、管理Beanではない普通のPOJOです。
helloは引数にPrimeFacesのNodeSelectEventを指定して、ここに画面で選択されたノード情報が入ってきます。

@AllArgsConstructor
@Getter @Setter
public class Actress {
    private String name;
    private int age;
    
    public Actress(String name){
        this.name = name;
    }
    
    public void hello(NodeSelectEvent event){
        Actress me = ((Actress)event.getTreeNode().getData());
        System.out.println("画面でクリックされたのは"+ me.name);
    }
}

で、このときに、EL式から呼んでるhelloに引数を渡したいケースがあったのですが、呼び出すメソッドとEL式に引数を追加しても、シグネチャが合わなくてメソッドがみつからない、と言われました。

<!-- 引数つけて呼び出したいけどhelloに引数追加しても呼べない -->
<p:ajax event="select" listener="#{act.hello(actressBean.checked)}" />

元ネタ

で、ググっていったら同じ質問が以下にありました。


java - JSF (and PrimeFaces) How to pass parameter to a method in ManagedBean - Stack Overflow

PrimeFaces Community Forum • View topic - Ajax Events with arguments

前者のStackOverflowの回答にevaluateExpressionGetを利用した回答があり、やってみたら動いたという。

ということで、こんな感じに。

public void hello(NodeSelectEvent event){
    Actress me = ((Actress)event.getTreeNode().getData());
    System.out.println("画面でクリックされたのは"+ me.name);
        
    //強引にActressBeanのchecked値をとってくる
    FacesContext context = FacesContext.getCurrentInstance();
    boolean beanChecked =
        context.getApplication().evaluateExpressionGet(context, "#{actressBean.checked}", boolean.class);
        
    System.out.println(beanChecked);
}

実際に今書いているコードに比べて上記の例だと、なんでそんな作りしてるんじゃ、って感じになりそうですが…良い例を書く時間が取れなかったのでここまで。

おまけ

@megascusさんはこのメソッドをご存じだったようで

とのこと…すごいなぁ。

3年くらいJSF使ってて初めて知りました(^^;

にほんブログ村 IT技術ブログへ
にほんブログ村 にほんブログ村 IT技術ブログ Javaへ
にほんブログ村