Hatena::ブログ(Diary)

cleverwareの日記 このページをアンテナに追加 RSSフィード

2009-11-11

Tagは属性を自分で変更してはいけない(例えばS2SubmitTag)

SAStrutsのSubmitTagは以下のように実装されている

    public int doEndTag() throws JspException {
        if (clientValidate) {
            FormTag formTag = (FormTag) findAncestorWithClass(this,
                    FormTag.class);
            if (formTag == null) {
                throw new JspTagException("FormTag not found.");
            }
            String actionFormName = formTag.getBeanName();
            StringBuilder sb = new StringBuilder();
            sb.append("var myForm = document.forms['").append(actionFormName)
                    .append("'];");
            sb.append("myForm.id='").append(actionFormName).append("_").append(
                    property).append("'; ");
            sb.append("return validate").append(
                    actionFormName.substring(0, 1).toUpperCase()).append(
                    actionFormName.substring(1)).append("_").append(property)
                    .append("(myForm);");
            String originalOnclick = getOnclick();
            if (originalOnclick == null
                    || originalOnclick.startsWith("var myForm")) {
                setOnclick(sb.toString());
            } else {
                setOnclick(originalOnclick + ";" + sb.toString());
            }
        }
        return super.doEndTag();
    }

setOnClick()で属性を書き換えて、super.doEndTag()の出力を書き換えているわけだが、こうするとJSPのTagキャッシュで問題が生じる。

たとえば、

<s:submit property="update" value="更新" onclick="hoge=1" clientValidate="true"/>

だった場合、これらの『指定された属性を含めたキーにして』JSPはタグをキャッシュに保存し、同じ属性が指定された場合にそのTagのインスタンスを再利用する。同じ属性が指定されている前提なので、当然『Tagのrelease()が呼ばれて再度属性が設定されるということはない』。つまり一度設定された属性はキャッシュに置かれたTagで使い回される。

このため、Tagの中で属性を書き換えるとおかしなことになってしまう。

上記の場合だと、1回目は期待通り onClick="hoge=1;var myForm = ....._update(myForm)" と出力されるが、

2回目は onClick="hoge=1;var myForm = .....;var myForm = ......;" となってしまう。つまり、属性を書き換えたために、その書き換えた内容に追加していく動作になってしまうわけだ。

これを回避するためには、タグの属性について不変であるようにするため、

    public int doEndTag() throws JspException {
        String originalOnclick = getOnclick();
        ....
            if (originalOnclick == null) {
                setOnclick(sb.toString());
            } else {
                setOnclick(originalOnclick + ";" + sb.toString());
            }
        }
        int ret = super.doEndTag();
        setOnClick(originalOnClick);
        return ret;
    }

こんな感じにしないといけない。

originalOnclick.startsWith("var myForm") の検査は冗長なので不要になる。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/cleverware/20091111/1257903061