7.10.2. Nablarch Validation

This chapter explains the validation function originally implemented by Nablarch.

Tip

As described in Input Value Check, it is recommended to use Bean Validation.

7.10.2.1. Function overview

7.10.2.1.1. Can Perform Validation and Type Conversion and Normalization of Values

Nablarch validation can perform validation, and type conversion and normalization of input values.

Since it can perform type conversion, the input values can be directly mapped to the numeric type (Integer or Long) of the Bean class. In addition, editing cancellation (normalization) of edited values can also be performed during type conversion.

For details, see Configuring Validators and Convertors for Use.

7.10.2.1.2. Can validate domains

Validation rules can be defined for each domain.

When using domain validation, it is only necessary to specify the domain name in the Bean class setter, which makes changing the validation rules easy.

For details, see Use domain validation.

7.10.2.1.3. Commonly Used Validators and Convertors are Provided

Nablarch provides commonly used validators and convertors as standard. For this reason, validation can be executed in the project only using Configuring Validators and Convertors for Use.

See the following links for validators and convertors provided by Nablarch.

7.10.2.2. Module list

<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-core-validation</artifactId>
</dependency>

<!-- Only when date validator and converter are used -->
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-common-date</artifactId>
</dependency>

<!-- Only when code value validator and converter are used -->
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-common-code</artifactId>
</dependency>

7.10.2.3. How to use

7.10.2.3.1. Configuring Validators and Convertors for Use

To enable validation, it is essential to register the validators and convertors being used in the component configuration file.

For the validators and convertors provided by Nablarch, see Commonly Used Validators and Convertors are Provided.

Important

If the validators and convertors are not configured, the validation function cannot be used.

Configuration example
<component name="validationManager" class="nablarch.core.validation.ValidationManager">
  <property name="convertors">
    <list>
      <!-- List the convertors used here -->
    </list>
  </property>
  <property name="validators">
    <list>
      <!-- List the validators used here-->
    </list>
  </property>

  <!--
  Other attributes omitted
  For details, see Javadoc of ValidationManager
   -->
</component>

7.10.2.3.2. Configure validation rules

Configure the annotations of validation rules in the properties (setter) of Bean class for validation. Also, note that annotations cannot be specified for a getter.(It is meaningless even if specified.)

Tip

If annotations are configured individually, errors during implementation and maintenance costs will increase. Hence, it is recommended to use the domain validation described below.

Implementation examples

Configure annotations by referring to the validators and convertors provided by Nablarch.

In this example, inputting the userName is required, and a maximum of 10 full-width characters are permitted. For birthday, 8 half-width digits are permitted. For age, up to 3 integer digits are permitted.

public class SampleForm {

  @Length(max = 10)
  @SystemChar(charsetDef = "Full-width character")
  @Required
  public void setUserName(String userName) {
      this.userName = userName;
  }

  @Length(min = 8, max = 8)
  @SystemChar(charsetDef = "Half-width character")
  public void setBirthday(String birthday) {
      this.birthday = birthday;
  }

  @Digits(integer = 3)
  public void setAge(Integer age) {
      this.age = age;
  }
}

7.10.2.3.3. Use domain validation

Show the configuration and implementation examples to use domain validation.

Creating Enum that defines the validation rules for each domain

To use domain validation, first create an Enum (domain Enum) with validation rules for each domain. This Enum must implement the DomainDefinition interface.

Each enumerator of the Enum will become a domain name. In the following example, two domains, NAME and DATE have been defined.

public enum SampleDomain implements DomainDefinition {

    @Length(max = 10)
    @SystemChar(charsetDef = "Full-width character")
    NAME,

    @Length(min = 8, max = 8)
    @SystemChar(charsetDef = "Half-width character")
    DATE;

    // Implementation of the method defined in the interface
    // Implementation contents should be exactly the same as this example
    @Override
    public Annotation getConvertorAnnotation() {
        return DomainValidationHelper.getConvertorAnnotation(this);
    }

    @Override
    public List<Annotation> getValidatorAnnotations() {
        return DomainValidationHelper.getValidatorAnnotations(this);
    }
}
Creating an annotation that represents a domain

Create an annotation that represents the domain. The domain Enum created above can be specified in the value attribute.

@ConversionFormat
@Validation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Domain {
    SampleDomain value();
}
Configure Domain in the Bean for Validation

Domain validation is performed by setting the annotations representing the domain created above.

In this example, the validation configured in SampleDomain.NAME is executed for userName. ※When a convertor is configured, the value is also converted by the convertor.

@Domain(SampleDomain.NAME)
public void setUserName(String userName) {
    this.userName = userName;
}
Configuration to enable domain validation

The following configurations are required to enable domain validation.

An example is shown below.

Configuration of DomainValidationHelper
<component name="domainValidationHelper"
    class="nablarch.core.validation.domain.DomainValidationHelper">

  <property name="domainAnnotation" value="sample.Domain" />

</component>
Configuration of DomainValidator
<component name="domainValidator"
    class="nablarch.core.validation.domain.DomainValidator">

  <!--
    DomainValidator should not be configured here. If configured, it will become a circular reference,
    and an error occurs during system repository initialization.
  -->
  <property name="validators">
    <list>
      <component-ref name="requiredValidator" />
    </list>
  </property>
  <property name="domainValidationHelper" ref="domainValidationHelper" />
</component>
Configuration of ValidationManager
<component name="validationManager" class="nablarch.core.validation.ValidationManager">
  <property name="validators">
    <list>
      <component-ref name="domainValidator" />
      <!-- Description of other validators is omitted -->
    </list>
  </property>
  <property name="domainValidationHelper" ref="domainValidationHelper" />
</component>
Configuration of initialization component

Configure DomainValidator and ValidationManager that have been configured above to the initialization list.

<component name="initializer"
    class="nablarch.core.repository.initialization.BasicApplicationInitializer">

  <property name="initializeList">
    <list>
      <component-ref name="validationManager" />
      <component-ref name="domainValidator" />
    </list>
  </property>
</component>
Behavior when Multiple Validation Rules are Configured for Domain Validation

If multiple errors are found in one input item in the domain validation, terminate scrutiny at the first error.

 public enum SampleDomain implements DomainDefinition {
   @Length(max = 10)
   @SystemChar(charsetDef = "Full-width character")
   NAME;
}

The NAME mentioned above does not perform SystemChar validation when a Length validation error occurs.

7.10.2.3.4. Inheriting the Bean for Validation

The Bean for validation can be inherited, but inheritance is not recommended for the following reasons.

When inheritance is performed easily, unexpected validation will be executed due to a change in the parent class, and annotation configuration must be performed while being aware of complex validation overwrite rules, which causes errors (bugs).

The following actions take place when Bean is inherited:

  • When only @PropertyName is added in the subclass, the validators and convertors of the parent class are used.
  • If even one validator annotation is added in the subclass, the validator annotation of the parent class is ignored and validators of the subclass will be used. The convertors of the parent class will be used.
  • If even one convertor annotation is added in the subclass, the convertor annotation of the parent class is ignored and convertors of the subclass will be used. The validators of the parent class will be used.
  • If both the validators and the convertors are configured in the subclass, all configurations of the subclass will be used.
  • The configurations of convertors in the parent class cannot be deleted in the subclass.

In the case of the following parent-child Bean, the validation of @Digits and @NumberRange is executed for the value property of ChildForm.

// Parent Form
public class ParentForm {
  @Digits(integer=5, fraction=3)
  public void setValue(BigDecimal value) {
      this.value = value;
  }
}

// Child Form
public class ChildForm extends ParentForm {
  @Override
  @NumberRange(min=100.0, max=20000.0)
  public void setValue(BigDecimal value) {
      super.setBdValue(value);
  }
}

7.10.2.3.5. Executing Validation

Validation can be executed by calling the method provided by ValidationUtil.

Implementation examples

First, implement a constructor that takes Map as an argument in the Bean for validation to generate a Bean object from the input value.

Next, implement a static method for performing validation in the Bean for validation. In this method, configure the @ValidateFor annotation, and specify an arbitrary value for identifying validation in the argument.

The validation is executed using ValidationUtil for the processing required for this method.

public class SampleForm {

  public SampleForm(Map<String, Object> params) {
      userName = (String) params.get("userName");
      birthDay = (String) params.get("birthDay");
      age = (Integer) params.get("age");
  }

  @Domain(SampleDomain.NAME)
  @Required
  public void setUserName(String userName) {
      this.userName = userName;
  }

  @Domain(SampleDomain.DATE)
  public void setBirthday(String birthday) {
      this.birthday = birthday;
  }

  @Domain(SampleDomain.AGE)
  public void setAge(Integer age) {
      this.age = age;
  }

  @ValidateFor("validate")
  public static void validate(ValidationContext<SampleForm> context) {
    // Validate userName, birthday and age
    ValidationUtil.validate(context, new String[] {"userName", "birthday", "age"});
  }
}

To validate an input value request using the Bean described above, use ValidationUtil as follows: In the case of a web application, validation can be performed more easily by Checking User Input Values for Web Applications.

// Execution of validation
// Checks the input parameter request using SampleForm.
//
// Specifies which validation method of SampleForm is used for validation in the last argument.
// Since validate is specified in this example,
// validation is executed using the validate method specified in the @ValidateFor annotation of SampleForm.
ValidationContext<SampleForm> validationContext =
        ValidationUtil.validateAndConvertRequest(SampleForm.class, request, "validate");

// If a validation error has occurred, an Exception is thrown with abortIfInvalid
validationContext.abortIfInvalid();

// Generates Form using a constructor that takes Map as an argument.
// (Form in which the input value request is converted can be acquired)
SampleForm form = validationContext.createObject();

7.10.2.3.6. Performing Explicit Execution of Validation

The Executing Validation was performed based on the annotations configured in Bean property (setter), but here, we will describe a method of executing the validation directly instead of configuring the annotations.

In principle, validation is performed using the Executing Validation method, use this method if it is necessary to perform validation individually. For example, if code management pattern is being used and validate is required to be performed by changing the pattern only for a specific screen, then execute validation individually.

Implementation examples

Explicit validation is executed from the method in which the @ValidateFor annotation for the Bean class is configured. Annotations that can be specified when executing explicit validation are limited to those that implement DirectCallableValidator. (Convertors cannot be specified.)

public class SampleForm {
  // Attributes omitted

  @ValidateFor("validate")
  public static void validate(ValidationContext<SampleForm> context) {

      ValidationUtil.validate(context, new String[]{"userName", "prefectureCode"});

      // Perform required check on the userName
      ValidationUtil.validate(context, "userName", Required.class);

      //  Specify annotation parameters in Map
      Map<String, Object> params = new HashMap<String, Object>();
      params.put("codeId", "1052");     // Code ID
      params.put("pattern", "A");       // Code pattern name that is used
      params.put("messageId", "M4865"); // Error message ID
      ValidationUtil.validate(context, "prefectureCode", CodeValue.class, params);
  }
}

Important

To perform explicit validation, it is essential to implement validation on the target items in advance. For details, see Executing Validation.

7.10.2.3.7. Performing Character Type Validation

The definition method for character type validation is the same as Bean Validation. For the detailed configuration method, see performing character type validation for Bean Validation. However, see the following since the configuration that allows surrogate pairs is different from Bean Validation.

Note that the annotation being used is @SystemChar, and the fully qualified name is different from Bean Validation (annotation name is the same).

Allowing Surrogate Pairs

This validation does not allow surrogate pairs by default. (They are not allowed even if the characters for surrogate pairs are explicitly defined in LiteralCharsetDef.)

To allow surrogate pairs, SystemCharValidator#allowSurrogatePair must be configured in the component configuration file as follows.

<component name="systemCharValidator" class="nablarch.core.validation.validator.unicode.SystemCharValidator">
  <!--  Allows surrogate pairs -->
  <property name="allowSurrogatePair" value="true"/>

  <!-- Other properties are omitted -->
</component>

7.10.2.3.8. Performing Correlation Validation

Correlation validation using multiple items is implemented using the method in which the Bean class annotation @ValidateFor is configured. This method first performs validation for each item, then executes validation using multiple items if no error occurs.

Implementation examples

In this example, correlation validation is performed using mailAddress and confirmMailAddress.

If an error has occurred during correlation validation, explicitly add the message ID indicating the message that must be notified to the user to ValidationContext.

public class SampleForm {

  @Domain(SampleDomain.MAIL)
  @Required
  public void setMailAddress(String mailAddress) {
      this.mailAddress = mailAddress;
  }

  @Domain(SampleDomain.MAIL)
  @Required
  public void setConfirmMailAddress(String confirmMailAddress) {
      this.confirmMailAddress = confirmMailAddress;
  }

  @ValidateFor("validate")
  public static void validate(ValidationContext<SampleForm> context) {
      // Implement validation of mailAddress and confirmMailAddress
      ValidationUtil.validate(context, new String[] {"mailAddress", "confirmMailAddress"});

      // Correlation not performed validation if an error has occurred
      if (!context.isValid()) {
          return;
      }

      // Generate form object and implement correlation validation
      SampleForm form = context.createObject();
      if (!Objects.equals(form.mailAddress, form.confirmMailAddress)) {
          // An error occurs when mailAddress and confirmMailAddress do not match
          context.addMessage("compareMailAddress");
      }
  }
}

7.10.2.3.9. Performing validation using a function that takes Bean array as the input, such as batch registration

There are cases where the same information is input multiple times, such as in batch registration. In such cases, a nested Bean is defined for the Bean for validation.

Configure the @ValidationTarget annotation in the nested Bean setter, and specify the size of the nested Bean. If the number of elements is fixed (determined during compile), specify it in the size attribute. If the number of elements is variable, configure the property name having a size in the sizeKey attribute.

In this example, SampleForm stores AddressForm as an array since the AddressForm information can be inputted in a batch. Also, sizeKey is used since the size is not determined during compile.

public class SampleForm {
    private AddressForm[] addressForms;
    // Size of addressForms
    // Send confidential items from the screen, etc.
    private Integer addressSize;

    @ValidationTarget(sizeKey = "addressSize")
    public void setAddressForms(AddressForm[] addressForms) {
        this.addressForms = addressForms;
    }

    @Domain(SampleDomain.SIZE)
    @Required
    public void setAddressSize(Integer addressSize) {
        this.addressSize = addressSize;
    }

    @ValidateFor("validate")
    public static void validate(ValidationContext<SampleForm> context) {
        ValidationUtil.validate(context, new String[] {"addressSize", "addressForms"});
    }
}

public class AddressForm {
    // Omitted
}

7.10.2.3.10. Changing validation items based on the selected value of the radio button and list box

By using the WebUtil class, validation items can be replaced based on the selected value of the radio button and list box.

In this example, when the value of form.radio sent from the screen is ptn1, only item1 is validated. For values other than ptn1, item1 and item2 are validated.

public class SampleForm {

    // Property is omitted

    @ValidateFor("validate")
    public static void validate(ValidationContext<SampleForm> context) {
        if (WebUtil.containsPropertyKeyValue(context, "form.radio", "ptn1")) {
            ValidationUtil.validate(context, new String[] {"item1"});
        } else {
            ValidationUtil.validate(context, new String[] {"item1", "item2"});
        }
    }
}

Tip

In this example, WebUtil.containsPropertyKeyValue is used to check even the sent value, but to just examine whether the radio button is checked, use WebUtil.containsPropertyKey.

7.10.2.3.12. To embed the item name in the message when a validation error occurs

To embed the item name in the message, specify the item name of the item for validation using the @PropertyName annotation.

Implementation examples

Use a pattern character to embed the item name in the message. Since the item name is always specified first, specify {0} where the item name is to be embedded.

required.message = Please enter a {0}.

Configure the annotation @PropertyName that sets the item name along with the validation annotation in the item for validation.

public class SampleForm {

    @Domain(SampleDomain.NAME)
    @Required
    @PropertyName("name")
    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Domain(SampleDomain.DATE)
    @PropertyName("birthday")
    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
}
Generated Message
In the above implementation, if a required error occurs in the username property, the message generated will be “Please enter name”.

7.10.2.3.13. Performing Type Conversion to Numeric Type

To convert the input value to a numeric type of the Bean class after validation, that item shall always require the @Digits annotation. ※In the case of domain validation, the configuration for domain Enum is required.

Let us assume that the convertors for converting to a numeric type are configured according to the procedure in Configuring Validators and Convertors for Use.

Implementation examples

In this example, it has been specified in setter, but we recommend specifying it in the domain Enum using domain validation.

public class SampleForm {

    @PropertyName("age")
    @Digits(integer = 3)
    public void setAge(Integer age) {
        this.age = age;
    }
}

7.10.2.3.14. Performing Correlation Validation with the Database

Correlation validation with the database is performed by business action.

See validation correlation with the Bean validation database for the reason why it is performed with business action.

7.10.2.3.15. Checking User Input Values for Web Applications

The user input values for web applications are checked using InjectForm Interceptor. For details, see InjectForm Interceptor.

7.10.2.4. Expansion example

7.10.2.4.1. To add a project-specific validator

The following steps are required to add a validator.

  1. Creating an annotation
  2. Creating a validator
  3. Registering the validator in a configuration file

The procedure is shown below.

Creating an annotation

Annotations must satisfy the following conditions.

  • Configure the @Validation annotation.
  • Configure ElementType.METHOD with the @Target annotation.
  • Configure RetentionPolicy.RUNTIME with the @Retention annotation.
@Validation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sample {
}
Creating a validator

The validator implements the Validator interface and implements the validation logic.

public class SampleValidator implements Validator {

  public Class<? extends Annotation> getAnnotationClass() {
      return Sample.class;
  }

  public <T> boolean validate(ValidationContext<T> context,
      // Omitted
  }
}
Registering the validator in a configuration file
See Configuring Validators and Convertors for Use.

7.10.2.4.2. To add a project-specific convertor

The following steps are required to add a convertor.

  1. Creating a convertor
  2. Registering the converter in a configuration file

The procedure is shown below.

Creating a convertor

The converter implements the Convertor interface and implements the type conversion logic, etc.

public class SampleConvertor implements Convertor {

    @Override
    public Class<?> getTargetClass() {
        return Short.class;
    }

    @Override
    public <T> boolean isConvertible(ValidationContext<T> context, String propertyName, Object propertyDisplayName,
            Object value, Annotation format) {

        boolean convertible = true;

        if (value instanceof String) {
            try {
                Short.valueOf((String) value);
            } catch (NumberFormatException e) {
                convertible = false;
            }
        } else {
            convertible = false;
        }

        if (!convertible) {
            context.addResultMessage(propertyName, "sampleconvertor.message", propertyDisplayName);
        }
        return convertible;
    }

    @Override
    public <T> Object convert(ValidationContext<T> context, String propertyName, Object value, Annotation format) {
        return Short.valueOf((String) value);
    }
}
Registering the converter in a configuration file
See Configuring Validators and Convertors for Use.

7.10.2.4.3. To change the method of generating a Bean object for validation

The following steps are required to change the method of generating a Bean object for validation.

  1. Create the implementation class FormCreator
  2. Add the component definition of the created class to ValidationManager.formCreator