Programming Thoughts
Struts 2 - Annotation-based Validation
Alternate Redesign

Improving a fix to a Struts 2 feature

Struts 2 is a popular MVC framework for Java-based web applications but its annotation-based validation doesn't properly work. So, an alternative was created but it has design limitations. This article considers a redesign to overcome them.

Design limitations

As described in Evaluation, the current design suffers from design flaws, summarised here.

  • Formatted/unformatted field pairs - two fields are more code than one, especially as field names like parsedId get in the way of bean property copiers, like BeanUtilsBean.copyProperties.
  • No automatic form formatting - although reduced to requiring a single line of code, that's still a line of code that can be forgotten.

Redesign Objectives

  • Interceptor based. Like the standard validators, adding to interceptor stacks adds to Actions without changing existing Action code.
  • Interceptor should be extensible. Odd and unanticipated requirements happen, so developers should be able to use a derived version of the interceptor instead.
  • Actual adjustment, validation and conversion should be separate classes. A statement of the obvious. The interceptor becomes the core framework with various adjusters, validators, and converters, collectively known as policies, attached to it. This is like the standard validator's design.
  • Annotation should use defaults and even be omitted where possible. As much boilerplate code should be removed as possible.
  • Policies don't apply if the form field value isn't set, unless overridden by annotation setting. The @Required annotation is the expected indicator where a field must be set.
  • Receiving form fields are character strings. This shifts formatting code to the form, where it's much more easily seen, and eliminates the per-class property files. It also eliminates the use of Struts data tags, besides property, and their inability to recognise failed conversion values.
  • Receiving form fields of the unformatted (non-string) data type should be supported and conversion failures should be placed on the Value Stack, much like standard validation.
  • Existing, alternate annotations on formatted (string) form fields, with paired unformatted field, should be maintained. Converter annotation remains required for conversion.
  • Converters should have a reverse conversion function for form formatting. The standard validators also do this.
  • Form formatting should be automatic, which places formatted values on the Value Stack to override access to displayed form fields.
  • Compatible with manual conversion of unformatted/formatted field pairs and validation. There are always weird exceptions to every rule, so it should not be required to write custom type converters or validator classes. Annotations can be omitted for a formatted form field and manual code written in the Action's validate function.
  • Manual parameter conversion and validation of unformatted form fields. Where an unformatted field isn't paired with a formatted field, and is an unrecognised type or marked for manual conversion, the form must manually read request parameters, convert, and declare conversions failures and messages.
  • Recognise ModelDriven. Use of ModelDriven allows a form to be stored in session as a POJO and recreated despite a refresh or redirection.
  • Annotation can configure that messages write to field errors, action errors or even action messages.
  • Abandon reliance on Struts UI tags. As HTML evolves, use of new features depends on support by the Struts UI tags, which is lagging at best. The FORM attribute for INPUT tags are still not supported, for example.
  • Configuration only in the form field annotations. Separate configuration files impedes readability, especially for others who don't understand Struts as well. This doesn't apply references to resource bundles, locale settings and the like as they already exist.
  • Custom conversion and validator annotations should even be able to use code in the same form. Referring to code in the same file aids readability.
  • A block of code in converters and validators should be able to report one of many error messages, whether from annotations or hardcoded. The standard validators have a single error condition but a few complex, custom ones can have multiple conditions and a different message for each.

Blockers of improved design

As described in Evaluation, an improved design can eliminate these limitations but suffer fatal problems that need to be solved.

  • Breaks existing code (view helpers) - some existing view helpers read the formatted half of a formatted/unformatted field pair, which will be eliminated.
  • Breaks existing code (form formatting) - most new forms created from records set the formatted field, not the unformatted field, so automated formatting from unformatted values will break this.
  • Prevents manual conversion and validation - where forms or validation logic are too complex for annotations, client code must read the string form values, so it can set a converted set of fields.
  • Avoids replacement parameter interceptor - this could be reinventing the wheel.

Next part

Continued in Existing Code.