そろそろかな

validation.xmlを??アノテーション化しようと思っていたら、すごいのを見つけてしまった。


その名も「無設定Strutsきしだのはてなさんが提供してくれてる。
これはすごくいいなー。


URLからAction、ActionForm、JSPを特定して実行できる。
自分の考えより何歩も先いってる。すごい!!


っていうことで、validation.xmlを??アノテーション化するのはやめ。あるものを作る必要はないし。
今はJ2SE1.4だから使えないけど、J2SE1.5(5.0?)にいずれバージョンアップして利用しよ。


ということで、今日はPARTYテーブルを編集する機能を作ってみる。
経験をつんできてるからすぐにできそうな予感。でもすぐにつまずきそうな感じもする。




AuthActionを作ったときと同じ感じで、Actionから作って足りないLogic処理を見つけて、Logicに処理の追加。っていう手順で作っていく。
テストクラスはつくならない予定・・・。




うーん。やっぱりテストクラスを作るべきかなー。悩む・・・。
とりあえず、前に進んでダメなら戻ってこよう。


まずは、PartyActionインターフェイスの作成。

public interface PartyAction {

    public static final String[] ACTION = { "path=/site/party",
            "name=partyForm", "scope=request", "unknown=false",
            "validate=false" };

    public static final String[] list_FORWARD = { "path=/pages/party/list.jsp" };

    public static final String[] detail_FORWARD = { "path=/pages/party/detail.jsp" };

    public static final String[] insert_FORWARD = { "path=/pages/party/insert.jsp" };

    public static final String[] insertConfirm_FORWARD = { "path=/pages/party/insertConfirm.jsp" };

    public static final String[] insertError_FORWARD = { "path=/pages/party/insert.jsp" };

    public static final String[] insertSuccess_FORWARD = { "path=/pages/party/detail.jsp" };

    public static final String[] update_FORWARD = { "path=/pages/party/update.jsp" };

    public static final String[] updateConfirm_FORWARD = { "path=/pages/party/updateConfirm.jsp" };

    public static final String[] updateError_FORWARD = { "path=/pages/party/update.jsp" };

    public static final String[] updateSuccess_FORWARD = { "path=/pages/party/detail.jsp" };

    public static final String[] deleteConfirm_FORWARD = { "path=/pages/party/deleteConfirm.jsp" };

    public static final String[] deleteError_FORWARD = { "path=/pages/party/detail.jsp" };

    public static final String[] deleteSuccess_FORWARD = { "path=/pages/party/detail.jsp" };

    // -----------------------------------------------------------------------
    
    public String list();
    
    public String detail();
    
    public String insertInit();
    
    public static final String insertConfirm_VALIDATE = "insertError";
    
    public String insertConfirm();
    
    public static final String insert_VALIDATE = "insertError";
    
    public String insert();
    
    public String updateInit();
    
    public static final String updateConfirm_VALIDATE = "updateError";
    
    public String updateConfirm();
    
    public static final String update_VALIDATE = "updateError";
    
    public String update();
    
    public String deleteConfirm();
    
    public String delete();

}

えらいことになってしまった。ダメな匂いが・・・
やっぱり1Action/1メソッドがいいような気がしてきた・・・
とりあえず作って、それからダメな点を修正していこう。


次はPartyActionの実装。

public class PartyActionImpl implements PartyAction {

    private PartyLogic partyLogic;

    private PartyDto partyForm;

    private List parties;

    public static final String errors_EXPORT = "errors";

    private Map errors = new HashMap();

    public PartyActionImpl(PartyLogic partyLogic) {
        this.partyLogic = partyLogic;
    }

    public String list() {
        parties = partyLogic.getAllParties();
        return "list";
    }

    public String detail() {
        partyForm = partyLogic.getParty(partyForm);
        return "detail";
    }

    public String insertInit() {
        // 編集するために必要となる情報(SelectBox情報等)はない。
        return "insert";
    }

    public String insertConfirm() {
        errors = partyLogic.validateInsert(partyForm);
        if (!errors.isEmpty()) {
            return "insertError";
        }
        return "insertConfirm";
    }

    public String insert() {
        errors = partyLogic.validateInsert(partyForm);
        if (!errors.isEmpty()) {
            return "insertError";
        }
        partyLogic.insert(partyForm);
        return "insertSuccess";
    }

    public String updateInit() {
        partyForm = partyLogic.getParty(partyForm);
        // 編集するために必要となる情報(SelectBox情報等)はない。
        return "update";
    }

    public String updateConfirm() {
        errors = partyLogic.validateUpdate(partyForm);
        if (!errors.isEmpty()) {
            return "updateError";
        }
        return "updateConfirm";
    }

    public String update() {
        errors = partyLogic.validateUpdate(partyForm);
        if (!errors.isEmpty()) {
            return "updateError";
        }
        partyLogic.update(partyForm);
        return "updateSuccess";
    }

    public String deleteConfirm() {
        errors = partyLogic.validateDelete(partyForm);
        if (!errors.isEmpty()) {
            return "deleteError";
        }
        return "deleteConfirm";
    }

    public String delete() {
        errors = partyLogic.validateDelete(partyForm);
        if (!errors.isEmpty()) {
            return "deleteError";
        }
        partyLogic.delete(partyForm);
        return "deleteSuccess";
    }

    // -----------------------------------------------------------------------

    public PartyDto getPartyForm() {
        return partyForm;
    }

    public void setPartyForm(PartyDto partyForm) {
        this.partyForm = partyForm;
    }

    public Map getErrors() {
        return errors;
    }

    public List getParties() {
        return parties;
    }
    
}

これもツッコミどころが満載って雰囲気を出してる。後から修正要って感じ。

たりないPartyLogicのメソッドを追加。
PartyLogic#validate???()ってメソッドは、入力パラメータ検証ではできない検証(accountが重複しているかのチェック等)を行うために作成。

public class PartyLogicImpl implements PartyLogic {

    private PartyDao partyDao;

    public PartyLogicImpl(PartyDao partyDao) {
        this.partyDao = partyDao;
    }

    public boolean existParty(PartyDto dto) {
        PartyDto party = partyDao.findByAccount(dto.getAccount());
        if (party == null) {
            return false;
        }

        return party.getPassword().equals(dto.getPassword());
    }

    public PartyDto getParty(String account) {
        return partyDao.findByAccount(account);
    }

    public PartyDto getParty(PartyDto dto) {
        return partyDao.findBean(dto);
    }

    public List getAllParties() {
        return partyDao.findAll();
    }

    public Map validateInsert(PartyDto dto) {
        Map result = new HashMap();
        PartyDto found = partyDao.findByAccount(dto.getAccount());
        if (found != null) {
            result.put("errors.account.duplication", new Object[] {});
        }
        return result;
    }

    public void insert(PartyDto dto) {
        partyDao.insert(dto);
    }

    public Map validateUpdate(PartyDto dto) {
        Map result = new HashMap();
        PartyDto found = partyDao.findBean(dto);
        if (found == null) {
            result.put("errors.update.notfound", new Object[] {});
        }
        
        found = partyDao.findByAccount(dto.getAccount());
        if (found != null && !found.getId().equals(dto.getId())) {
            result.put("errors.account.duplication", new Object[] {});
        }
        return result;
    }

    public void update(PartyDto dto) {
        partyDao.update(dto);
    }

    public Map validateDelete(PartyDto dto) {
        Map result = new HashMap();
        PartyDto found = partyDao.findBean(dto);
        if (found == null) {
            result.put("errors.delete.notfound", new Object[] {});
        }
        return result;
    }

    public void delete(PartyDto dto) {
        partyDao.delete(dto);
    }

}

こっちの問題点はわかりやすい

  1. valiedate???()の返却値となるMapの内容がAction内の処理形式に依存してる。
  2. insert()、update()、delete()する前にそれぞれのvalidate???()を呼ぶべきではないか。
  3. valiedate???()はテストすべき。

1つ目は仕方ないってことにしよう。下手にこっても大変になるだけのような気がするし。
2つ目と3つ目はそうしよう。
そこで、PartyLogicImplとPartyActionImplの修正。これは簡単。


次はPartyLogicImpl#validate???()関連のテスト作成。

public class PartyLogicTest extends S2DaoTestCase {
    
    private PartyLogic logic;

    private PartyDto dto = new PartyDto(new Integer(2));

    public PartyLogicTest(String arg0) {
        super(arg0);
    }

    public void setUp() {
        include("study/xxx/dicon/alldao.dicon");
        include("study/xxx/dicon/party.dicon");
    }
    
    public void testValidateInsertOKTx() {
        deleteTable(PartyDto.TABLE);
        
        PartyDto ins = new PartyDto();
        ins.setAccount("INSTEST");
        ins.setPassword("INSPWD");
        ins.setName("INSテスト");
        
        Map errors = logic.validateInsert(ins);
        assertTrue(errors.isEmpty());
    }
    
    public void testValidateInsertNGAccountDuplicationTx() {
        deleteTable(PartyDto.TABLE);
        readXlsWriteDb("study/xxx/dao/PartyTestData.xls");
        
        PartyDto ins = logic.getParty(dto);
        
        Map errors = logic.validateInsert(ins);
        assertFalse(errors.isEmpty());
        assertTrue(errors.containsKey("errors.account.duplication"));
    }
    
    public void testValidateUpdateOKTx() {
        deleteTable(PartyDto.TABLE);
        readXlsWriteDb("study/xxx/dao/PartyTestData.xls");
        
        PartyDto up = logic.getParty(dto);
        
        Map errors = logic.validateUpdate(up);
        assertTrue(errors.isEmpty());
    }
    
    public void testValidateUpdateNGNotfoundTx() {
        deleteTable(PartyDto.TABLE);
        
        PartyDto up = new PartyDto(new Integer(1));
        up.setAccount("UPTEST");
        
        Map errors = logic.validateUpdate(up);
        assertFalse(errors.isEmpty());
        assertTrue(errors.containsKey("errors.update.notfound"));
    }
    
    public void testValidateUpdateNGAccountDuplicationTx() {
        deleteTable(PartyDto.TABLE);
        readXlsWriteDb("study/xxx/dao/PartyTestData.xls");
        
        PartyDto up = logic.getParty(dto);
        up.setAccount("TEST");
        
        Map errors = logic.validateUpdate(up);
        assertFalse(errors.isEmpty());
        assertTrue(errors.containsKey("errors.account.duplication"));
    }
    
    public void testValidateDeleteOKTx() {
        deleteTable(PartyDto.TABLE);
        readXlsWriteDb("study/xxx/dao/PartyTestData.xls");
        
        Map errors = logic.validateDelete(dto);
        assertTrue(errors.isEmpty());
    }
    
    public void testValidateDeleteNGNotfoundTx() {
        deleteTable(PartyDto.TABLE);
        
        Map errors = logic.validateDelete(dto);
        assertFalse(errors.isEmpty());
        assertTrue(errors.containsKey("errors.delete.notfound"));
    }

}

テスト実行。グリーンバー。
先にPartyLogicImplを作っていたから、わざとassertTrue()のところをassertFalse()に変えて失敗することも確認。


次は、DICONファイルとstruts-config.xml、MessageResources.propertiesへの記述の追加。
struts-config.xmlへform-beanの追加を忘れないようにしないと。
中途半端な改造をしたから。


一番山場となるJSPを作る作業。
これは大変だ。
重複する内容が多くなるけど、\pages\party\フォルダにこれだけのJSPをつくならないといけない。

これは大変だ。
まずは、list.jspの作成。
html:linkタグの使い方を忘れた。変な感じだったってことは覚えてるけど。調べてみる。


思ってたほど変でもなかった。
list.jspはこんな感じかな

<%@page contentType="text/html; charset=SHIFT_JIS" %>

<%@taglib uri="/tags/c" prefix="c" %>
<%@taglib uri="/tags/struts-bean" prefix="bean" %>
<%@taglib uri="/tags/struts-html" prefix="html" %>

<c:set var="path" value="/site/party"/>

<html:html>
<head>
  <title><bean:message key="title.party.list" /></title>
</head>
<body>

<html:form action="${path}" method="POST">

<!-- メニューボタン -->
<table class="btn-menu"><tr>
  <td nowrap>
    <html:submit property="${path}:list" styleClass="btn-menu">
      <bean:message key="button.list" />
    </html:submit><html:submit property="${path}:insertInit" styleClass="btn-menu">
      <bean:message key="button.insert.new" />
    </html:submit>
  </td>
</tr></table>
  
<table class="outer">
  <tr><td>
    <html:errors />
  </td></tr>

  <tr><td>
    <table class="list">
      <tr class="list-header">
        <th><bean:message key="form.party.account" /></th>
        <th><bean:message key="form.party.name" /></th>
        <th><br /></th>
      </tr>
      <c:forEach var="party" items="${parties}" varStatus="status">
        <tr class="<c:out value="list-${status.count % 2}" />">
          <td class="text"><c:out value="${party.account}" /></td>
          <td class="text"><c:out value="${party.name}" /></td>
          <td class="text">
            <html:link page="/do${path}?${path}:detail"
                       paramId="id" paramName="party" paramProperty="id">
              <bean:message key="button.detail.s" />
            </html:link>
            <html:link page="/do${path}?${path}:updateInit"
                       paramId="id" paramName="party" paramProperty="id">
              <bean:message key="button.update.s" />
            </html:link>
            <html:link page="/do${path}?${path}:deleteConfirm"
                       paramId="id" paramName="party" paramProperty="id">
              <bean:message key="button.delete.s" />
            </html:link>
          </td>
        </tr>
      </c:forEach>
    </table>
  </td></tr>
</table>

</html:form>

</body>
</html:html>

それとメニュー欄にパーティ一覧画面へのリンクを追加。


Tomcatを起動して動作確認。
うまく表示できた。JSPエラーなし。今日も調子がいいみたい。


次はdetail.jspこれも追加して動作確認。

javax.servlet.ServletException: キー "form.party.name" に対応するメッセージが見つかりません

ってJSPエラーがでたけど、そこだけだった。やっぱり調子がいい。
detail.jspはこんな感じ。

<%@page contentType="text/html; charset=SHIFT_JIS" %>

<%@taglib uri="/tags/c" prefix="c" %>
<%@taglib uri="/tags/struts-bean" prefix="bean" %>
<%@taglib uri="/tags/struts-html" prefix="html" %>

<c:set var="path" value="/site/party"/>
<c:set var="form" value="${partyForm}"/>

<html:html>
<head>
  <title><bean:message key="title.party.detail" /></title>
</head>
<body>

<html:form action="${path}" method="POST">
<!-- メニューボタン -->
<table class="btn-menu"><tr>
  <td nowrap>
    <html:submit property="${path}:list" styleClass="btn-menu">
      <bean:message key="button.list" />
    </html:submit><html:submit property="${path}:insertInit" styleClass="btn-menu">
      <bean:message key="button.insert.new" />
    </html:submit>
  </td>
</tr></table>
  
<table class="outer">
  <tr><td>
    <html:errors />
  </td></tr>

  <html:hidden property="id" />
  <html:hidden property="account" />
  <html:hidden property="password" />
  <html:hidden property="name" />
  <html:hidden property="parentId" />
  <tr><td>
    <table class="detail">
      <tr>
        <th class="detail"><bean:message key="form.party.account" /></th>
        <td class="text"><c:out value="${form.account}" /></td>
      </tr>
      <tr>
        <th class="detail"><bean:message key="form.party.password" /></th>
        <td class="text"><c:out value="${form.password}" /></td>
      </tr>
      <tr>
        <th class="detail"><bean:message key="form.party.name" /></th>
        <td class="text"><c:out value="${form.name}" /></td>
      </tr>
    </table>
  </td></tr>
</table>

<table class="btn"><tr>
  <td nowrap>
    <html:submit property="${path}:updateInit" styleClass="btn-menu">
      <bean:message key="button.update" />
    </html:submit><html:submit property="${path}:deleteConfirm" styleClass="btn-menu">
      <bean:message key="button.delete" />
    </html:submit>
  </td>
</tr></table>

</html:form>

</body>
</html:html>

パスワードも表示してしまうという強烈なdetail.jsp。修正要。


なんとなくだけど、insert.jspの前にupdate.jspを先に作る。
うまくできた。調子にのってupdateConfirm.jspの作成。
「戻る」ボタンに対応するメソッドがない。PartyAction#updateBack()を作って対応。

できた。でも、
更新画面(各種情報を変更して「変更」ボタンクリック)→確認画面(内容を確認して「OK」ボタンクリック)→更新後の情報での詳細画面
って感じで画面遷移しているんだけど、最後の詳細画面表示時に「変更しました」っていうメッセージがほしい。


困ったなー。簡単に実装するためには、PartyActionImplで

    public String update() {
        errors = partyLogic.update(partyForm);
        if (!errors.isEmpty()) {
            return "updateError";
        }
        errors.put("messages.complete.update", new Object[] {});
        return "updateSuccess";
    }

ってすればいいと思うけど、正常終了しているのにエラーメッセージを利用するのは気が引ける。


どうしよう。


変数名をerrorsからmessagesに変更すればOKかも。なんか屁理屈っぽいけど。結構いい案かも。
これでいこう。さっそくPartyActionImplを修正。


そしてTomcat起動して動作確認。
うまくできた。

変更機能は完了。
次は、追加機能としてinsert.jspとinsertConfirm.jspを作成。

JSPエラーなくできた。順調。

最後は、deleteConfirm.jspの作成。
これも順調に行くかなーと思ったら、詳細情報が表示されていない。取得してないからだ。
PartyActionImpl#deleteConfirm()を少し修正。

    public String deleteConfirm() {
        messages = partyLogic.validateDelete(partyForm);
        if (!messages.isEmpty()) {
            return "deleteError";
        }
        partyForm = partyLogic.getParty(partyForm);
        return "deleteConfirm";
    }

削除後に表示する詳細画面で削除した情報が表示されてる・・・。表示しないように修正しよう。

    public String delete() {
        messages = partyLogic.delete(partyForm);
        if (!messages.isEmpty()) {
            return "deleteError";
        }
        messages.put("messages.complete.delete", new Object[] {});
        partyForm = null;
        return "deleteSuccess";
    }

PartyActionImpl#delete()後には、partyFormにnullを設定して、detail.jspはpartyFormがemptyなら詳細情報を表示しないように修正。


動作確認。できた。これで一通りは完成かな。
細かいところを修正しよう。


PartyActionでのinsertSuccess_FORWARDのpathをdetailメソッドを呼び出すように変更。
今回は詳細情報表示が単純だけど、複雑になってきたときに困ると思うから。Actionの連鎖ができるか確認。

    public static final String[] insertSuccess_FORWARD = { "path=/pages/party/detail.jsp" };
    public static final String[] insertSuccess_FORWARD = { "path=/do/site/party?/site/party:detail" };


動作確認。エラー。しかも

java.lang.ClassCastException
	org.apache.struts.util.RequestUtils.lookupActionForm(RequestUtils.java:210)
	org.apache.struts.util.RequestUtils.createActionForm(RequestUtils.java:179)

なぜ?


ちょっと調査。
requestからActionFormを取り出したときのキャストで発生してる。
ActionFormの処理はS2Strutsから独自で変更したところだから、自分で組み込んだバグだ。

XxxActionPropertyBinderImplにバグがあるのか?



あった。
POJOActionからexportするときに特別な処理をしていないからPOJOFormのままexportしてた。
XxxActionPropertyBinderImplとXxxActionExecuteProcessorImplのバグだ。S2Strutsが提供してくれているActionExecuteProcessorImplだったら発生しなかった。
修正しないと。でも、その前にテストメソッドを作るのが先。

ActionPropertyBindingTest#testExportActionForm()を追加。

    public void testExportActionForm() throws Exception {
        ExportActionFormActionImpl action = new ExportActionFormActionImpl();
        Object form = new BeanValidatorForm(
                new ExportActionForm("noUpdateForm"));
        ActionMapping mapping = new MockActionMapping();
        mapping.setType(ExportActionFormAction.class.getName());
        mapping.setName("exportActionForm");
        mapping.setScope("request");

        getRequest().setAttribute(Globals.MAPPING_KEY, mapping);
        getRequest().setAttribute("exportActionForm", form);

        ActionForward actionForward = actionExecuteProcessor
                .processActionExecute(getRequest(), getResponse(), action,
                        form, mapping);

        assertTrue(getRequest().getAttribute("exportActionForm") instanceof BeanValidatorForm);

        BeanValidatorForm beanValidatorForm = (BeanValidatorForm) getRequest()
                .getAttribute("exportActionForm");
        WrapDynaBean dynaBean = (WrapDynaBean) beanValidatorForm.getDynaBean();
        ExportActionForm exportForm = (ExportActionForm) dynaBean.getInstance();
        assertEquals("updateForm", exportForm.getTestMessage());
    }

ExportActionFormActionImplは

public class ExportActionFormActionImpl implements ExportActionFormAction {

    private ExportActionForm exportActionForm;

    public String test() {
        exportActionForm = new ExportActionForm("updateForm");
        return "success";
    }

    public ExportActionForm getExportActionForm() {
        return exportActionForm;
    }

    public void setExportActionForm(ExportActionForm exportActionForm) {
        this.exportActionForm = exportActionForm;
    }
    
}

テスト実行。


エラー。

[ESSR0046]コンポーネント(interface study.xxx.extention.struts.processor.ExportActionFormAction)が見つかりません

DICONファイルに記述を忘れてた。
記述を追加して再度テスト実行。


期待した失敗。
次はグリーンバーにするためにXxxActionPropertyBinderImplの修正。



自前でBeanValidatorFormを作る場合は、newするだけでいいのかなー。
BeanValidatorForm#setServletContext()が気になる。


XxxActionPropertyBinderImplの修正内容は

    public void exportProperties(Object action, S2Container container,
            BeanDesc beanDesc) {

        for (int i = 0; i < beanDesc.getPropertyDescSize(); ++i) {
            PropertyDesc propertyDesc = beanDesc.getPropertyDesc(i);
            if (propertyDesc.hasReadMethod()) {
                Object var = propertyDesc.getValue(action);
                if (isActionFormProperty(container, propertyDesc)) {
                    exportActionFormProperty(container, propertyDesc, var);
                } else if (isSessionProperty(beanDesc, propertyDesc)) {
                    exportSessionProperty(container, propertyDesc, var);
                } else if (isErrorsProperty(beanDesc, propertyDesc)) {
                    exportErrorsProperty(container, propertyDesc, var);
                } else {
                    exportProperty(container, propertyDesc, var);
                }
            }
        }
    }

    private void exportActionFormProperty(S2Container container,
            PropertyDesc propertyDesc, Object var) {

        HttpServletRequest request = container.getRequest();
        ActionMapping mapping = ActionMappingUtil.get(request);

        ActionFormUtil.setActionForm(request, mapping, var);
    }

で、ActionFormUtilに

    public static void setActionForm(HttpServletRequest request,
            ActionMapping mapping, Object var) {

        ActionForm form;
        if (var instanceof ActionForm) {
            form = (ActionForm) var;
        } else {
            form = createBeanValidatorForm(request, mapping, var);
        }
        
        if (REQUEST.equals(mapping.getScope())) {
            request.setAttribute(mapping.getAttribute(), form);
        } else {
            HttpSession session = request.getSession();
            session.setAttribute(mapping.getAttribute(), form);
        }
    }

    private static BeanValidatorForm createBeanValidatorForm(
            HttpServletRequest request, ActionMapping mapping, Object var) {

        BeanValidatorForm result = new BeanValidatorForm(var);
        ActionForm oldForm = getActionForm(request, mapping);

        // TODO 自前でBeanValidatorFormを作ったあとの処理についてはもう一度考える
        MultipartRequestHandler multipartHandler = oldForm
                .getMultipartRequestHandler();
        if (multipartHandler != null) {
            result.setServlet(multipartHandler.getServlet());
            result.setMultipartRequestHandler(multipartHandler);
        }

        return result;
    }

を追加した。
ActionFormUtil#createBeanValidatorForm()は適当。勉強不足を露呈してる・・・。
ソースを追う限りではこれで十分だと思ったけど、どうだか。


テスト実行。グリーンバー。だけど、すっきりしないなー。


このテストの時にActionFormをクリアした場合の動作が気になったからついでにActionPropertyBindingTest#testClearActionForm()を追加しよう。

    public void testClearActionForm() throws Exception {
        ClearActionFormActionImpl action = new ClearActionFormActionImpl();
        Object form = new BeanValidatorForm(
                new ClearActionForm());
        ActionMapping mapping = new MockActionMapping();
        mapping.setType(ClearActionFormAction.class.getName());
        mapping.setName("clearActionForm");
        mapping.setScope("request");

        getRequest().setAttribute(Globals.MAPPING_KEY, mapping);
        getRequest().setAttribute("clearActionForm", form);

        ActionForward actionForward = actionExecuteProcessor
                .processActionExecute(getRequest(), getResponse(), action,
                        form, mapping);

        assertTrue(getRequest().getAttribute("clearActionForm") == null);
    }
public class ClearActionFormActionImpl implements ClearActionFormAction {

    private ClearActionForm clearActionForm;

    public String test() {
        clearActionForm = null;
        return "success";
    }

    public ClearActionForm getClearActionForm() {
        return clearActionForm;
    }

    public void setClearActionForm(ClearActionForm clearActionForm) {
        this.clearActionForm = clearActionForm;
    }
}

エラー。NullPointerException


やっぱり、さっき作ったActionFormUtilがダメみたい。
nullの場合はremoveAttribute()するように修正。ついでにメソッド名も変更。


テスト実行。グリーンバー。



やっとTomcatを起動して動作確認。


insertConfirmでJSPエラー。
${form}はクラス "org.apache.struts.validator.BeanValidatorForm"だから、getAccount()なんてありません。
っていってる。

さっきの修正が影響してるんだ。修正しないと。


ちょっとラッキー
JSP内の処理で

<c:set var="form" value="${partyForm}"/>

ってしてたから、この1行を修正すればOKだ。ラッキー
POJOFormのとり方はついさっきActionFormUtilの修正で見たから覚えてる。

<c:set var="form" value="${partyForm.dynaBean.instance}"/>

みたいに修正して再度確認。


できた。

他のJSPも修正しないと。




やっとPartyActionでのinsertSuccess_FORWARDのpathをdetailメソッドを呼び出すように変更後の動作確認。

    public static final String[] insertSuccess_FORWARD = { "path=/do/site/party?/site/party:detail" };

だめだ。空ページが表示される。


なぜだ???

pathの記述の最後に"="がないからかなー。下のように変更してチャレンジ。

    public static final String[] insertSuccess_FORWARD = { "path=/do/site/party?/site/party:detail=" };


今度は、IDがうまくわたらず空の詳細画面が表示された。ちょっと進歩したけど、やっぱりダメだ。
うーん。
Actionの連鎖って、この他にもたくさん問題がでそうだし、あんまりしないほうがいいよって聞いた(見た?)ことあるから(本当か?)、これ以上こだわるのはやめよう。
この問題をクリアしても、次は「ActionFormの内容が上書きされた」とかで悩むと思うし。
何個かバグもなくせたことを成果としよう。
やめ。ってことで素直に戻す。一回目の挫折。

    public static final String[] insertSuccess_FORWARD = { "path=/pages/party/detail.jsp" };


そして今回作った機能が動くかもう一度確認して、気持ちをよくしてから今日の勉強は終了。