Seasar DI Container with AOP

概要

在S2Struts中生成一个可以使用注释的Validation,需要完成以下的处理。

我们将通过生成一个Validation来检验2个输入值是否相同的处理步骤中,对这一工作进行说明。

用Struts生成一个新的Validation

追加一个新的Struts(commons validator)的Validation,需要进行以下处理。

  • 生成一个检验用的类
  • 向validator-rules.xml文件中追加设定信息
Struts Validator Guide资料里,有记载使用Struts追加一个新的Validation的方法,也可以作为参考。

检验2个输入值是否相同的类如下所示。

package org.seasar.struts.examples.validator;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.util.ValidatorUtils;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.validator.Resources;

public class MyFieldChecks {

    public static boolean validateTwoFields(Object bean,
            ValidatorAction validatorAction, Field field,
            ActionMessages errors, Validator validator,
            HttpServletRequest request) {

        String value = ValidatorUtils.getValueAsString(bean, field.getProperty());
        String sProperty2 = field.getVarValue("secondProperty");
        String value2 = ValidatorUtils.getValueAsString(bean, sProperty2);

        if (!GenericValidator.isBlankOrNull(value)) {
            try {
                if (!value.equals(value2)) {
                    errors.add(field.getKey(), Resources.getActionMessage(
                            validator, request, validatorAction, field));

                    return false;
                }
            } catch (Exception e) {
                errors.add(field.getKey(), Resources.getActionMessage(
                        validator, request, validatorAction, field));
                return false;
            }
        }

        return true;
    }
}

首先,取得用于检验的属性值。 其次,取得在validation设定中的"secondProperty"变量里指定的属性名,从而取得其值。 最后对2个值进行比较以便确认是否是正确的值。 在这里,关键的是必须有一个"secondProperty"变量。 这个变量,在生成Validation注释的时候声明为属性值。

然后将上述的类追加到validator-rules.xml文件中。 validator标签的name属性(validator name)里指定的内容,是注释名及其相关。 在S2Struts中,按以下的规则变换注释名,判断其是否和validator name相一致,从而取得其值。

  • 注释名的开头字母为小写字母
  • 注释名以"Type"结尾的场合则将"Type"除去
例如,Hoge注释的场合,validator name为"hoge",FooType注释的场合,validator name则为"foo"。

因为这回的注释名是TwoFields,所以name就是"twoFields"。在validator-rules.xml文件中追加以下的内容。

      <validator name="twoFields"
            classname="org.seasar.struts.examples.validator.MyFieldChecks"
               method="validateTwoFields"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionMessages,
                       org.apache.commons.validator.Validator,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.twoFields"/>

msg属性中因为指定了"errors.twoFields",这个信息Key追加到application.properties中。

errors.twoFields={0} has to have the same value as the confirm field.

到此为止,是用Struts生成一个新的Validation的过程。

生成Validation注释

生成Validation注释,需要进行以下的处理。

  • 定义一个注释
  • 生成ConfigRegister

S2Struts可以提供的注释有3种,也即常量注释,Tiger注释,Backport175注释。 作为常量注释利用的场合,不必定义注释。 作为Tiger注释利用的场合,需要指定org.seasar.struts.validator.annotation.tiger.ValidatorTarget作为meta注释,按如下方法作成。

package org.seasar.struts.examples.validator.annotation.tiger;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.seasar.struts.validator.annotation.tiger.ValidatorTarget;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ValidatorTarget
public @interface TwoFields {

    String secondProperty();

}

作为Backport175注释利用的场合,需要指定org.seasar.struts.validator.annotation.backport175.ValidatorTarget作为meta注释,按如下方法作成。

package org.seasar.struts.examples.validator.annotation.backport175;

/**
 * @org.seasar.struts.validator.annotation.backport175.ValidatorTarget
 */
public interface TwoFields {

    String secondProperty();

}

这回为了进行值的检验,需要指定secondProperty,所以如上述记载的那样。 在validation设定中如果不需要变量,就没必要定义属性,也没必要做成以下的ConfigRegister。

注释中取得的值,需要生成ConfigRegister类并作设定,因此生成一个TwoFieldsConfigRegisterImpl。

TwoFieldsConfigRegisterImpl类是org.seasar.struts.lessconfig.validator.config.ConfigRegister的实装类。 register方法的参数为field和parameter。 field是一个对象,在其内部,对字段进行妥当性检查以及生成错误信息时,拥有可以替换的妥当性检查名单和信息名及信息值。 也即收藏Validation内容的对象。 parameter是一个Map,以注释中定义的方法名为key,方法的返回值为value。

package org.seasar.struts.examples.validator.config;

import java.util.Map;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.Var;
import org.seasar.struts.lessconfig.validator.config.ConfigRegister;

public class TwoFieldsConfigRegisterImpl implements ConfigRegister {

    public void register(Field field, Map parameter) {
        String secondProperty = (String) parameter.get("secondProperty");

        Var var = new Var();
        var.setName("secondProperty");
        var.setValue(secondProperty);
        field.addVar(var);
    }

}

生成的ConfigRegister需要作为组件进行登记。 登记时的组件名,遵循与validator name的时候同样的注释名变换规则,在注释名之后加"ConfigRegister"作为组件名。 例如,Hoge注释的场合为"hogeConfigRegister",FooType注释的场合则是"fooConfigRegister"。

因为这回作为组件进行登记,生成一个新的validator.dion。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="twoFieldsConfigRegister"
            class="org.seasar.struts.examples.validator.config.TwoFieldsConfigRegisterImpl"/>
</components>

然后在app.dicon里追加include,读取validator.dion的内容。

    <include path="validator.dicon"/>

到此为止,是用S2Struts生成新的注释的过程。

至此,生成一个使用注释的Validation的过程就完成了。

动作的确认

为了验证生成的TwoFields注释,做以下的处理。

  • 生成View
  • 生成Action
  • 生成Form

首先生成一个View。这里是把JSP用作一个View。这是为了确认结果简单做成的一个JSP。

<%@ page contentType="text/html;charset=Windows-31j" language="java" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://www.seasar.org/tags-s2struts" prefix="s2struts" %>

<html>
<head>
  <title>Two Fields Test</title>
</head>
<body>
  <html:errors />

  <html:form action="/twoFields" method="POST">
    <s2struts:page/>
    <table>
      <tr>
        <td>field</td>
        <td><html:text property="field"/></td>
      </tr>
      <tr>
        <td>confirm field</td>
        <td><html:text property="confirmField"/></td>
      </tr>
    </table>
    <html:submit/>
  </html:form>
</body>
</html>

然后生成一个Action。这回用的是无设定功能的POJO的Action(详细内容请参考「无设定S2Struts指南」)。 Action的接口和实装类如下所示。

package org.seasar.struts.examples.validator;

public interface TwoFieldsAction {
    
    String execute();

}
package org.seasar.struts.examples.validator;

public class TwoFieldsActionImpl implements TwoFieldsAction {

    public String execute() {
        return "success";
    }

}

虽然Action的处理仅仅是返回"success"值,但用于TwoFields注释的动作确认已经足够了。

Action作为组件需要登记。新生成的twoFields.dicon如下所示。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
	"http://www.seasar.org/dtd/components21.dtd">
<components>
	<component name="twoFieldsAction"
			class="org.seasar.struts.examples.validator.TwoFieldsActionImpl"
			instance="request" />
</components>

然后在app.dicon里追加include以读取twoFields.dicon。

    <include path="org/seasar/struts/examples/dicon/twoFields.dicon"/>

最后生成Form。作为例子,以下记述了常量注释和Tiger注释时的2个Form。 请用所使用到的注释来生成Form。

package org.seasar.struts.examples.validator;

import java.io.Serializable;

public class TwoFieldsForm implements Serializable {

    private static final long serialVersionUID = 1L;

    private String field = "";

    private String confirmField = "";
    
    public String getField() {
        return field;
    }
    
    public static final String field_VALIDATOR_0 = "required";

    public static final String field_VALIDATOR_1 = "twoFields, secondProperty = confirmField";

    public static final String field_VALIDATOR_ARGS = "Field, resource=false";

    public void setField(String field) {
        this.field = field;
    }

    public String getConfirmField() {
        return confirmField;
    }

    public static final String confirmField_VALIDATOR = "required";

    public static final String confirmField_VALIDATOR_ARGS = "ConfirmField, resource=false";

    public void setConfirmField(String confirmField) {
        this.confirmField = confirmField;
    }

}
package org.seasar.struts.examples.validator;

import java.io.Serializable;

import org.seasar.struts.examples.validator.tiger.TwoFields;
import org.seasar.struts.validator.annotation.tiger.Args;
import org.seasar.struts.validator.annotation.tiger.Required;

public class TwoFieldsForm implements Serializable {

    private static final long serialVersionUID = 1L;

    private String field = "";

    private String confirmField = "";
    
    public String getField() {
        return field;
    }
    
    @Required
    @TwoFields(secondProperty="confirmField")
    @Args(keys = "Field", resource = false)
    public void setField(String field) {
        this.field = field;
    }

    public String getConfirmField() {
        return confirmField;
    }

    @Required
    @Args(keys = "ConfirmField", resource = false)
    public void setConfirmField(String confirmField) {
        this.confirmField = confirmField;
    }

}

这样就可以进行确认了。 启动Tomcat,从浏览器访问http://localhost:8080/s2struts-example/pages/twoFields.jsp进行动作确认。

生成一个新的使用注释的Validation,与通常的处理相比,也许是一件很麻烦的事情,不过一旦完成的话,在Form中就很容易使用了。 如此说来,生成注释时的辛苦也有了足够的回报。

你是否愿意尝试做一个自己用的Validation呢?