Challenge Java EE !

Java EEを中心に趣味や仕事における開発メモを書いています。

レスポンスを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>
にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ Javaへ
にほんブログ村
QLOOKアクセス解析