谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

S2JSFでWebParts その5

複数のInitActionが呼び出せない件について、一歩前進。


複数のHTMLファイルを1画面に表示させたいので

<span m:inject="s:insert" m:src="hoge.html"/>

こんな風にしてHTMLファイルを呼び出す予定だけど、
呼び出される側のHTML(hoge.html)では、initActionが実行されなくて困っている。


と言うわけで、InitAction周辺のソースコードを読んでみて、下のように理解した。

  • InitActionのinvokeは、org.seasar.jsf.runtime.ViewRendererImpl#executeInitActionで実行される。
  • このexecuteActionは、ViewRendererImpl#renderViewから呼ばれている。
  • ViewRendererImpl#renderViewは、org.seasar.jsf.lifecycle.LifeCylcleImplから呼ばれる。

→ つまり、InitActionの呼び出し元であるrenderViewは、一回のリクエストについて、原則一度しか呼ばれない。


一方、s:insert辺りはどうかと言うと、

  • s:insertタグを解釈する実体は、org.seasar.jsf.processor.InsertProcessor#processInclude
  • processIncludeでは特にInitActionの呼び出しを行なっていない。

→ ここでInitActionを強引に実行させることができるハズ。


ということで、ViewRendererImplを参考にしながら
InsertProcessorに手を入れてみたら、insertされる側のinitActionが実行された。
変更箇所のソースはこんな感じ。

	protected void processInclude(JsfContext jsfContext, Tag parentTag,
			String src) throws JspException {

		S2Container container = SingletonS2ContainerFactory.getContainer();
		ViewTemplateFactory factory = (ViewTemplateFactory) container
				.getComponent(ViewTemplateFactory.class);
		ViewTemplate template = factory.getViewTemplate(src);
		ViewProcessor viewProcessor = (ViewProcessor) template
				.getRootTagProcessor();
		InsertProcessor insertProcessor = viewProcessor
				.getInsertProcessor(null);

		// -- 変更 BEGIN
		String initAction = viewProcessor.getInitAction();
		FacesContext context = FacesContext.getCurrentInstance();
		if (initAction != null) {
			executeInitAction(context, initAction);
		}
		// -- 変更 END

		insertProcessor.process(jsfContext, parentTag);
	}

	// -- メソッド追加 BEGIN
	protected void executeInitAction(FacesContext context, String initAction) {
		Application app = context.getApplication();
		MethodBinding mb = app.createMethodBinding(initAction, null);
		InvokeUtil.invoke(mb, context);
	}
	// -- メソッド追加 END

ViewRendererImplでは、ErrorPageManager(エラー発生時に特定のページを表示させる)の
処理が入ってたけど、実験なのでその辺りは割愛。


とりあえず、複数のInitActionを呼ぶための方法が一つはできた。




ところで、まだ未追跡なんだけど、
initActionがnull以外をreturnした場合には、renderViewメソッドが繰り返して呼ばれるみたい。
遷移する場合には、次の画面をレンダリングするって事だと思うけど。


とすると、
1つめのPartsのInitAction → Interceptorで戻り値を書き換え → 2つめのPartに遷移
 → 2つめのPartsのInitAction → Interceptorで戻り値を書き換え → 3つめのPartsに遷移・・・
なんて風にうまくやれば、InitActionを連鎖的に呼び出せるかも知れない。


こっちの方がエレガントなので、また後から調べてみよう。