Shale View Controller その1

ずいぶんと間が開きましたが,今回はView Controllerです.

JSFでは,画面と対になるバッキングビーンを持ちます.
このバッキングビーンは,前回のClayでも使用していたように,いわゆるPOJOを使用します.
バッキングビーンは,画面の入力項目を保持したり,サブミットボタンなどの処理を記述したりします.

View Controllerは,バッキングビーンに使用することができます.
View Contorllerを使用すると,JSFのライフサイクルに拡張ポイントを設定することができます.

View Controllerが設定できる拡張ポイントは4つです.

  • init()
  • preprocess()
  • prerender()
  • destroy()

JSFは,6つのライフサイクルを持っています.
Requestを受け取ってから,Responseを返すまでに,JSFはこのライフサイクルに沿って処理を行います.

  • Restore View
  • Apply Request Values
  • Process Validation
  • Update Model Values
  • Invoke Application
  • Render Response

init

init()は,Restore Viewフェーズの前処理に呼ばれます.
Shaleでは,このフェーズでView Controllerの生成やpostbackの設定などを行っています.

preprocess

preprocess()は,Restore Viewフェーズの後処理,Apply Request Valuesフェーズの前に,ポストバック時のみ呼ばれます.
※ポストバックについては,後述

prerender

prerender()は,Render Responseフェーズの前処理にて呼ばれます.
画面描写の直前に呼ばれます.

destroy

destroy()は,バッキングビーンの消滅時に呼ばれます.
Render Responseフェーズの後になります.

Postback

ポストバックとは,同一画面遷移になります.
例えば,hello画面があったとします.
hello画面を始めて描写したとき,JSFではコンポーネントツリーが作成されます.
この時点では,当然ポストバックとはなりません.
画面にサブミットボタンがあった場合,そのボタンをクリックすると,
JSFでは,同一画面遷移をいったん行います.この場合,ポストバック処理となります.
Invoke Applicationフェーズでnullを返し,次画面遷移した場合も当然ポストバック処理となります.
次の画面に遷移した場合も,次の画面の1回目も描写では,ポストバックとはなりません.
絵があれば,わかりやすいのですが,ここでは割愛します.

なんとなくそうなんだと思うか,JSFを調べてみてください.

View Controllerでは,isPostback()メソッドで,ポストバックを受け取ることができます.
.NETなどでは,ポストバックが存在するらしいですが,JSFにポストバックがなかったのは,残念なことでした.
Shaleでは,isPostback()により,1回目の表示時での処理を記述することが可能となりました.

View Controllerを使用するには?

View Controllerを使用するには,org.apache.shale.view.ViewControllerをimplementsするだけで使用可能となります.
又は,org.apache.shale.view.AbstractViewControllerを継承することでも使用可能です.

AbstractViewController

AbstractViewControllerを継承すると,他にも便利なメソッドを使用することができます.
AbstractViewControllerは,AbstractFacesBeanを継承しています.
AbstractFacesBeanは,JSFを使用する上での便利なヘルパメソッドが用意してあります.

JSF関係

  • getApplication()
  • getApplicationMap()
  • getExternalContext()
  • getFacesContext()
  • getLifecycle()

リクエストやレスポンス関係

  • getRequestHeaderMap()
  • getRequestMap()
  • getRequestParameterMap()
  • getSessionMap()
  • getRequestParameter(String)
  • getRequestParameterValues(String)

ログ関係

  • log(String)
  • log(String, Throwable)

メッセージ関係(すみません.訂正します.2006/07/26)

  • info(String)
  • info(UIComponent, String)
  • warn(String)
  • warn(UIComponent, String)
  • error(String)
  • error(UIComponent, String)
  • fatal(String)
  • fatal(UIComponent, String)

バッキングビーン関係

  • getBean(String)
  • setBean(String, Object)
  • getValue(String)
  • setValue(String, Object)

その他

  • retrieveData(String)
  • saveData(String, Object)
  • erase()

その中で,いくつかよく使いそうな物を説明します.

getBean,getValue

getBean()は,登録してあるバッキングビーンやマネージドビーンを取得することができます.
faces-config.xmlのmanaged-bean-nameで登録してある名前を指定します.

getValue()は,EL式を使用して,値を取り出します.
Ex: getValue("#{hello.name}");
このようにすると,helloで登録してあるバッキングビーンのnameの値を取り出すことができます.

setは,その逆で,ビーンや値を設定します.

erase

このメソッドは,コンポーネントツリーのルートから,所持している子供のコンポーネントのすべてのSubmittedValueをクリアーします.
このメソッドを見たとき,「おおっ」と思いました.
JSFをラッピングしたことがある人は,きっと同じ反応をしてくれるような気がします.
使い方は...また今度...

info,error,warn,fatal 2006/07/26 追記

JSFのエラーメッセージを設定します.
このメソッドを使用すると,

<h:messages />
<h:message />

で出力されます.

ShaleとView Controller

Shaleでは,ライフサイクルの始めにバッキングビーン(View Controller)を作成します.
探し出すルールは,ディフォルトではorg.apache.shale.view.impl.DefaultViewControllerMapperが使用されます.

ディフォルトのルールは,/hello.facesを例にすると
1.先頭の'/'を取ります
2.'.'以下を取ります
上記例では,'hello'のバッキングビーンを探します.

/hello/hello.facesを例にすると,
1.先頭の'/'を取ります
2.途中の'/'を'$'に置き換えます
3.'.'以下を取ります
上記例では,'hello$hello'のバッキングビーンを探します.

Shaleでは,上記ルールに基づいてバッキングビーンを登録しないといけません.

ルールの変更は,web.xmlにて行うことができます.

<context-param>
    <param-name>org.apache.shale.view.VIEW_MAPPER</param-name>
    <param-value>sample.MyViewControllerMapper</param-value>
</context-param>

おまけ

View Controllerのinit()とdestroy()は,昨日まではバグっていて,
1回のRequestで2回,3回と呼ばれていましたが,本日修正されたようです.
Request ScopeのView Controllerは,適切に1回ずつ呼ばれるようになりました.

しかし,Session ScopeにView Controllerを置くと,init()とdestory()が呼ばれません...
(それどころか,prerender()すら呼ばれない...)
このバグもそのうち修正されるでしょう.

View Controllerが適切に呼ばれない不具合の理由はわかっていました.
まぁ,destroy()を呼ぶタイミングっていうのが,かなり難しいんですよね...
View Controllerは,とりあえずrequest scopeにおいておきましょう.