どこまでできるかな
今日は、Actionメソッド単位のvalidateを実装する。
久しぶりにStrutsのサイトにいったら、バージョンがアップしてた。
1.2.4から1.2.7へ。
Release Notesを見て確認。
commons-collections.jarはいらなくなったみたい。
うーん。英語は読めないからよくわからん。気にするのはやめよう。
以下のjarとtld、xmlを更新。
struts.jar struts-el.jar struts-bean-el.tld struts-html-el.tld struts-logic-el.tld validator-rules.xml
動くか確認。
LOGON/LOGOFFはうまく動いた。Strutsバージョンアップ完了。
次はvalidateのInterceptorを作る。
Actionインターフェースで
public static final String execute_VALIDATE = "/pages/error.jsp" public String execute();
って感じで記述があったら、validateを行うように実装しよう。
まずはテストクラスを作らないと
public class ValidateInterceptorTest extends S2TestCase { private ActionExecuteProcessor actionExecuteProcessor; protected void setUp() throws Exception { super.setUp(); include("s2struts.dicon"); include("ValidateInterceptorTest.dicon"); } protected void tearDown() throws Exception { super.tearDown(); } public void testValidator() throws Exception { ValidateAction action = (ValidateAction) getComponent(ValidateAction.class); ValidateMessageForm form = new ValidateMessageForm(); ActionMapping mapping = new MockActionMapping(); mapping.setType(ValidateAction.class.getName()); mapping.setName("validateMessageForm"); ActionForward actionForward = actionExecuteProcessor .processActionExecute(getRequest(), getResponse(), action, form, mapping); assertEquals("called", form.getTestMessage()); } }
あと利用する、ActionとActionFormを作成して、
public interface ValidateAction { public static final String test_VALIDATE = "/pages/error.jsp"; public String test(); }
public class ValidateMessageForm extends ValidatorForm { private String testMessage = ""; public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { testMessage = "called"; return null; } public String getTestMessage() { return testMessage; } }
DICONファイルを記述してテスト実行。
期待した失敗。
次はValidateInterceptorを作ってテストを通す作業。
ValidateInterceptorを作るためにTraceInterceptorを参考にしてみる。
うーん。もうちょっと参考がほしいなー。
S2StrutsのActionExecuteProcessorImplとBindingUtilも参考にすればできそうだ。
public class ValidateInterceptor extends AbstractInterceptor { private static final String REQUEST = "request"; private static final String VALIDATE_SUFFIX = "_VALIDATE"; public Object invoke(MethodInvocation invocation) throws Throwable { String validateName = invocation.getMethod().getName() + VALIDATE_SUFFIX; ComponentDef componentDef = getComponentDef(invocation); BeanDesc beanDesc = new BeanDescImpl(componentDef.getComponentClass()); if (beanDesc.hasField(validateName)) { if (!validate()) { Field validateField = beanDesc.getField(validateName); return (String) FieldUtil.get(validateField, null); } } return invocation.proceed(); } public boolean validate() { S2Container container = SingletonS2ContainerFactory.getContainer(); HttpServletRequest request = container.getRequest(); ActionMapping mapping = (ActionMapping) request .getAttribute(Globals.MAPPING_KEY); Object form = getActionForm(request, mapping); if (form == null || !(form instanceof ActionForm)) { return true; } ActionErrors errors = ((ActionForm) form).validate(mapping, request); if (errors == null || errors.isEmpty()) { return true; } request.setAttribute(Globals.ERROR_KEY, errors); return false; } public Object getActionForm(HttpServletRequest request, ActionMapping mapping) { if (REQUEST.equals(mapping.getScope())) { return request.getAttribute(mapping.getAttribute()); } else { HttpSession session = request.getSession(); return session.getAttribute(mapping.getAttribute()); } } }
こんな感じかな。DICONファイルを修正してテスト実行。
うーん。障害トレースを見るとActionMappingがnullみたい。
あらかじめ、requestにActionMappingとActioFormをsetAttribute()しないといけないのかも。
あと、ActionMapping#setScope()もしてなかった。これも追加しよう。
public void testActionMessage() throws Exception { ValidateAction action = (ValidateAction) getComponent(ValidateAction.class); ValidateMessageForm form = new ValidateMessageForm(); ActionMapping mapping = new MockActionMapping(); mapping.setType(ValidateAction.class.getName()); mapping.setName("validateMessageForm"); mapping.setScope("request"); getRequest().setAttribute(Globals.MAPPING_KEY, mapping); getRequest().setAttribute("validateMessageForm", form); ActionForward actionForward = actionExecuteProcessor .processActionExecute(getRequest(), getResponse(), action, form, mapping); assertEquals("called", form.getTestMessage()); }
テスト実行。グリーンバー。
無理やり通したって感じだ。
作ってて思ったことが2点。
ValidateInterceptor#validate()でfalseのときに戻す値はforward名だから、"/pages/error.jsp"ではなくて"error"みたいにしないといけない。
ValidateInterceptor#validate()は、後で修正が必要になる予感。
ActionFormUtilに抜き出しておこう。そうしたらValidateInterceptorが単純になるし。
抜き出した後テスト実行。グリーンバー。問題なし。
次はいよいよAuthActionへの適用。
まずは、allaop.diconのactionInterceptorChainにさっき作ったValidateInterceptorを追加。
AuthActionインターフェースの修正。
そしてTomcatを起動して手動による確認。
validateしていない。ダメだ。
なぜ?
よくわからん。
こういうときは、System.out.printlnデバッグだ。
POJOのActionFormだからみたい。
それでvalidateできていない。
もうちょっと調査しよう。
XxxActionExecuteProcessorImpl#toBeanFromActionForm()で、requestにあるBeanValidatorFormをPOJOFormに変えてる。
それでか。
どうしよう
XxxActionExecuteProcessorImpl#toBeanFromActionForm()をしなくてもうまくPOJOActionにセットされるように変更。
どんなテストをつくればいいんだろう。
public void testBindingActionForm() throws Exception { BindingActionFormActionImpl action = new BindingActionFormActionImpl(); Object form = new BeanValidatorForm(new BindingActionForm("test")); ActionMapping mapping = new MockActionMapping(); mapping.setType(BindingActionFormAction.class.getName()); mapping.setName("bindingActionForm"); mapping.setScope("request"); getRequest().setAttribute("bindingActionForm", form); ActionForward actionForward = actionExecuteProcessor .processActionExecute(getRequest(), getResponse(), action, form, mapping); assertEquals("Binding=OK Request=OK ", action.getTestMessage()); }
public class BindingActionFormActionImpl implements BindingActionFormAction { private BindingActionForm bindingActionForm = null; private String testMessage = ""; public String test() { HttpServletRequest request = SingletonS2ContainerFactory.getContainer() .getRequest(); if (bindingActionForm != null && bindingActionForm.getMessage().equals("test")) { testMessage += "Binding=OK "; } if (request.getAttribute("bindingActionForm") instanceof BeanValidatorForm) { testMessage += "Request=OK "; } return "success"; } public BindingActionForm getBindingActionForm() { return bindingActionForm; } public void setBindingActionForm(BindingActionForm bindingActionForm) { this.bindingActionForm = bindingActionForm; } public String getTestMessage() { return testMessage; } }
なんか変なテストだな・・・。こんなもんか?
テスト実行。期待通りの失敗。
次はグリーンバーにするためにXxxActionExecuteProcessorImplとXxxActionPropertyBinderImplを変更。
XxxActionPropertyBinderImpl#importProperties()は以下のように変更。
public void importProperties(Object action, S2Container container, BeanDesc beanDesc) { importParameter(action, container); for (int i = 0; i < beanDesc.getPropertyDescSize(); i++) { PropertyDesc propertyDesc = beanDesc.getPropertyDesc(i); if (isActionFormProperty(container, propertyDesc)) { importActionFormProperty(action, container, propertyDesc); } else { importProperty(action, container, propertyDesc); } } } private void importActionFormProperty(Object action, S2Container container, PropertyDesc propertyDesc) { HttpServletRequest request = container.getRequest(); ActionMapping mapping = (ActionMapping) request .getAttribute(Globals.MAPPING_KEY); Object form = ActionFormUtil.getActualActionForm(request, mapping); propertyDesc.setValue(action, form); } private boolean isActionFormProperty(S2Container container, PropertyDesc propertyDesc) { ActionMapping mapping = (ActionMapping) container.getRequest() .getAttribute(Globals.MAPPING_KEY); return propertyDesc.getPropertyName().equals(mapping.getAttribute()); }
あとXxxActionExecuteProcessorImpl#toBeanFromActionForm()とsetActionForm()を削除。
そしてテスト実行。
・・・まただ。requestにActionMappingをsetAttribute()してないのが原因。
今まで作ったテストメソッドにも追加してやらないといけない・・・
処理を追加して再度テスト実行。
グリーンバー。できたかも。
さっそくTomcatを起動して手動による確認。
できた。やった。
XxxActionPropertyBinderImplが汚くなってきたからきれいにしよう。
・・・あんまりきれいにならない。
テストクラスのメソッドも移動したり、クラス名を変えたりしよう。
XxxActionExecuteProcessorImplTestって結局、XxxActionExecuteProcessorImplのテストっていうよりは、
適切なActionExecutorが呼び出せているかのテストときちんとActionにPropertyをImport/Exportできているかのテストになっている。
ActionExecutorTestとActionPropertyBindingTestに分けよう。
それにあわせてテストメソッド名や利用しているActionの名前も変えよう。