Page-Driven StrutsでのS2Strutsの利用

jpetstoreを改造しながらnobeans版Page-Driven Strutsの実装を探っているんですが、
ここ数日、CartPageでの注文量の更新処理でActionの実行前にNullPointerExceptionになってしまうエラーで悩んでいました。

java.lang.NullPointerException
	org.apache.struts.validator.BeanValidatorForm.get(BeanValidatorForm.java:221)
	org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:386)
	org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:340)
	org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:664)
	org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:715)
	org.apache.commons.beanutils.BeanUtilsBean.setProperty(BeanUtilsBean.java:884)
	org.apache.commons.beanutils.BeanUtilsBean.populate(BeanUtilsBean.java:811)
	org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:298)
	org.apache.struts.util.RequestUtils.populate(RequestUtils.java:493)
	org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:805)
	org.seasar.struts.processor.S2RequestProcessor.processPopulate(S2RequestProcessor.java:107)
	org.seasar.struts.processor.InputValueFormProcessorImpl.create(InputValueFormProcessorImpl.java:38)
	org.seasar.struts.processor.S2RequestProcessor.processInputValueFormCreate(S2RequestProcessor.java:156)
	org.seasar.struts.processor.AcceptorImpl.process(AcceptorImpl.java:65)
	org.seasar.struts.processor.S2RequestProcessor.process(S2RequestProcessor.java:43)
	org.apache.struts.action.ActionServlet.process(ActionServlet.java:1194)
	org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
〜(省略)〜

素直に読むとバリデーション関係で失敗しているようです。

POJOフォームのsetterメソッドの形式が違うのかとか、スコープの問題なのかとか、色々悩んで回り道をしましたが、やっと解決方法がわかりました。

nobeans版のPage-Driven Strutsでは、POJOフォームや無設定化を利用するためにS2Strutsを全面的に利用しています。

で、ついさっきわかったのが、正常に実行できるときは ProcessPojoFormInterceptor$SerializeBeanValidatorForm がPOJOフォーム(ページクラス)のラッパクラスとして使われているんですが、エラーが起きるときは、InputValueForm というクラスがラッパとして使われているという違いです。

どうもこのInputValueFormに対するRequestUtils.populate()がうまくいかないため、バリデーションロジックのところでNullPointerExceptionが発生してしまったということのようです。
ちなみにこのInputValueFormはバリデーション専用のための一時的フォームみたいです。これでバリデーションが成功すれば、次に開発者が用意したフォームが取得されてパラメータ値が適用されるということみたい。

CartPageでの更新UIは、nested:iterateタグを使ってループしているそれぞれのアイテムにたいして同時にテキストフィールドを編集できるタイプのもので、パラメータキーは

  cartItems[0].item.itemId = EST-18

のような感じになります。
これをInputValueFormにうまくpopulate()できないのが原因だという気がしますが、あまり自信ありません。

結局どうやってこれを回避したかというと、S2Struts標準でS2RequestProcessor#processActionForm()に対するアスペクトにProcessPojoFormInterceptorを適用してPOJOフォームを作成する処理を行っているのと同じように、processInputValueFormCreate()メソッドにも同じアスペクトを適用してPOJOフォームをバリデーションのときにも使ってしまおうという方法です。要はInputValueFormを使わないことにしたわけです。

	<component name="requestProcessor"             class="org.seasar.struts.processor.S2RequestProcessor" instance="prototype">
		<aspect pointcut="processActionForm,processInputValueFormCreate">processActionFormInterceptor</aspect>
	</component>

という感じで。

とりあえずこれでNullPointerExceptionを回避することはできました。
が、副作用などはまだわかりません。
今は実際にはバリデーションをかけてないので、その辺りを利用しようとするとまた何か問題が発生する可能性もあります。

一応、一歩進んだということでメモ。