Automatic validation of method calls with JSR-303 (Appendix-C of the specification)

The recently approved Bean Validation Standard (JSR-303) left one great (and requested) feature out of the specification: method validation. This proposal defined an additional API for the Validator with methods that allowed validation of method/constructor parameters as well as the return value of methods. Thankfully, even though this spec didn’t make it to the final approved document, all constraint annotations accept parameter as target, so the door was left open for it to be implemented as an extra feature.
methodvalidation.tar

Apache BeanValidation

There are currently 2 implementations of the standard, Hibernate Validator and Apache BeanValidation (formerly agimatec-validator, and as of this march an Apache Incubator project). Of these two, only Apache BeanValidation supports the additional method validation API, so it is the only choice if you need that feature, and it’s what I will use as base for this example.

Method validation API

The proposed additional methods in the bean validation are the following:

<T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Method method,
                                                       Object[] parameterValues,
                                                       Class<?>... groups);
 
<T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Method method,
                                                   Object parameterValue,
                                                   int parameterIndex,
                                                   Class<?>... groups);
 
<T> Set<ConstraintViolation<T>> validateReturnedValue(Class<T> clazz, Method method,
                                                       Object returnedValue,
                                                       Class<?>... groups);
 
<T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz,
                                                    Constructor constructor,
                                                    Object[] parameterValues,
                                                    Class<?>... groups);
 
 
<T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz,
                                                   Constructor constructor,
                                                   Object parameterValue,
                                                   int parameterIndex,
                                                   Class<?>... groups);

So, to validate the parameters of a method call, one would call validateParameters with the holder class, the method description and the parameter values as parameters, and the output would be similar than when validating a bean.

And how do you specify the constraints? In the method declaration, as in this example:

@NotNull
@NotEmpty
public String operation(@NotNull @Pattern(regexp="[0-9]{2}") String param) {
   // Your code
   return val;
}

This enhanced method declaration indicates that the param value cannot be null, and must be matched by the regular expression [0-9]{2}. In the same way, the value returned by the function cannot be null or an empty string.

Automatic validation using AspectJ

Being one good example of a crosscutting concern, validation code can easily pollute all your application code and maintenance can become really difficult. So, a good way to implement it automatically is using AspectJ. This way, you will decide in a single place (the pointcut) what method and constructors you want to be validated, and the validation code will also be centralized in a single place (the advice).

The aspect implementing this functionality is as follows:

package net.carinae.methodvalidation;
 
import java.util.Arrays;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.ValidatorFactory;
import org.apache.bval.jsr303.extensions.MethodValidator;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * Enforces correct parameters and return values on the adviced methods and constructors.
 * <p>
 * NOTE: Currently only works with Apache BeanValidation.
 * 
 * @author Carlos Vara
 */
public aspect MethodValidationAspect {
 
	final static Logger logger = LoggerFactory.getLogger(MethodValidationAspect.class);
 
	static private ValidatorFactory factory;
 
	static {
		factory = Validation.buildDefaultValidatorFactory();
	}
 
	static private MethodValidator getMethodValidator() {
		return factory.getValidator().unwrap(MethodValidator.class);
	}
 
 
	pointcut validatedMethodCall() : execution(@ValidatedMethodCall * *(..));
 
	pointcut validatedConstructorCall() : execution(@ValidatedConstructorCall * .new(..));
 
	pointcut validatedReturnValue() : validatedMethodCall() && execution(!void *(..));
 
 
	/**
	 * Validates the method parameters.
	 */
	before() : validatedMethodCall() {
 
		MethodSignature methodSignature = (MethodSignature)thisJoinPoint.getSignature();
 
		logger.trace("Validating call: {} with args {}", methodSignature.getMethod(), Arrays.toString(thisJoinPoint.getArgs()));
 
		Set<? extends ConstraintViolation<?>> validationErrors = getMethodValidator().validateParameters(thisJoinPoint.getThis().getClass(), methodSignature.getMethod(), thisJoinPoint.getArgs());
 
		if ( validationErrors.isEmpty() ) {
			logger.trace("Valid call");
		}
		else {
			logger.warn("Invalid call");
			RuntimeException ex = buildValidationException(validationErrors);
			throw ex;
		}
 
	}
 
 
	/**
	 * Validates the constructor parameters.
	 */
	before() : validatedConstructorCall() {
 
		ConstructorSignature constructorSignature = (ConstructorSignature)thisJoinPoint.getSignature();
 
		logger.trace("Validating constructor: {} with args {}", constructorSignature.getConstructor(), Arrays.toString(thisJoinPoint.getArgs()));
 
		Set<? extends ConstraintViolation<?>> validationErrors = getMethodValidator().validateParameters(thisJoinPoint.getThis().getClass(), constructorSignature.getConstructor(), thisJoinPoint.getArgs());
 
		if ( validationErrors.isEmpty() ) {
			logger.trace("Valid call");
		}
		else {
			logger.warn("Invalid call");
			RuntimeException ex = buildValidationException(validationErrors);
			throw ex;
		}
	}
 
 
	/**
	 * Validates the returned value of a method call.
	 * 
	 * @param ret The returned value
	 */
	after() returning(Object ret) : validatedReturnValue() {
 
		MethodSignature methodSignature = (MethodSignature)thisJoinPoint.getSignature();
 
		logger.trace("Validating returned value {} from call: {}", ret, methodSignature.getMethod());
 
		Set<? extends ConstraintViolation<?>> validationErrors = getMethodValidator().validateReturnedValue(thisJoinPoint.getThis().getClass(), methodSignature.getMethod(), ret);
 
		if ( validationErrors.isEmpty() ) {
			logger.info("Valid call");
		}
		else {
			logger.warn("Invalid call");
			RuntimeException ex = buildValidationException(validationErrors);
			throw ex;
		}
 
	}
 
 
	/**
	 * @param validationErrors The errors detected in a method/constructor call.
	 * @return A RuntimeException with information about the detected validation errors. 
	 */
	private RuntimeException buildValidationException(Set<? extends ConstraintViolation<?>> validationErrors) {
		StringBuilder sb = new StringBuilder();
		for (ConstraintViolation<?> cv : validationErrors ) {
			sb.append("\n" + cv.getPropertyPath() + "{" + cv.getInvalidValue() + "} : " + cv.getMessage());
		}
		return new ValidationException(sb.toString());
	}
 
}

I have defined 3 simple pointcuts (to advice parameter validation of method and constructors, and return value of methods), and I have used 2 custom made interfaces to annotate the methods in which I want validation to be performed. You may of course want to tweak those pointcuts to adapt to your environment. The code of the 2 interfaces is included in the packaged project.

Some gotchas

Take care that the current implementation of the method validation API is still experimental. As of this writing, many constraints still don’t work (I used a patched build for the @Size and @Pattern constraints to work), but it’s just a matter of time that all the features available for bean validation work as well for parameter validation.

If you want to start from a template, I have attached a simple maven project that shows the use of this technique and has the aspect and needed interfaces code in it.
Download it here: methodvalidation.tar.gz

Integration of JSR 303 bean validation standard and Wicket 1.4

In this entry I will show a way to integrate the new JSR 303 bean validation standard with Wicket 1.4. The resulting example form will have AJAX callbacks to inform the user promptly about validation errors and these messages will be internationalized according to the locale associated with the user’s session. Spring 3 will be used to manage the Validator instance.

To get in perspective, in this example a user registration form (inside of a panel) will be created. The form will have 4 inputs: email, password, password verification and user age. When a user fills an input, an AJAX callback to the server will be done to validate that input. In case of a validation error, the reported errors will appear next to that input.

The UserRegistrationPanel

This panel will contain two components: the form and a feedback panel where the errors which are not associated to a single input will be reported (when the 2 supplied passwords don’t match for example).

The panel markup is as follows:

<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd">         
<body>
<wicket:panel>
 
    <form wicket:id="registrationForm">
        <fieldset>        
            <div wicket:id="validatedEmailBorder" class="entry">
                <label for="email">Your e-mail:</label>
                <input wicket:id="email" name="email" type="text" />
            </div>            
            <div wicket:id="validatedPasswordBorder" class="entry">
                <label for="password">Choose password:</label>
                <input wicket:id="password" name="password" type="password" />
            </div>            
            <div wicket:id="validatedPasswordVerificationBorder" class="entry">
                <label for="passwordVerification">Re-type password:</label>
                <input wicket:id="passwordVerification" name="passwordVerification" type="password" />
            </div>            
            <div wicket:id="validatedAgeBorder" class="entry">
                <label for="age">Your age:</label>
                <input wicket:id="age" name="age" type="text" />
            </div>        
            <input type="submit" value="Register!"/>
        </fieldset>
    </form>
 
    <div wicket:id="feedback" class="feedback"></div>
 
</wicket:panel>
</body>
</html>

Only one thing to explain in here, the inputs are surrounded with a border component. This way, it will be easy to control the extra markup needed to show the input related validation errors.

And now, the associated code UserRegistrationPanel.java:

public class UserRegistrationPanel extends Panel {
 
	public UserRegistrationPanel(String id) {
		super(id);
 
		// Insert the form and the feedback div
		RegistrationForm regForm;
		add(regForm = new RegistrationForm("registrationForm"));
		add(new FeedbackPanel("feedback", new ComponentFeedbackMessageFilter(regForm)).setOutputMarkupId(true));
	}
 
	public final class RegistrationForm extends StatelessForm<NewUser> {
 
		public RegistrationForm(String id) {
			super(id, new CompoundPropertyModel<NewUser>(new NewUser()));
 
			TextField<String> emailInput = new TextField<String>("email");
			add(new InputValidationBorder<NewUser>("validatedEmailBorder", this, emailInput));
 
			PasswordTextField passwordInput = new PasswordTextField("password");
			add(new InputValidationBorder<NewUser>("validatedPasswordBorder", this, passwordInput));
 
			PasswordTextField passwordVerificationInput = new PasswordTextField("passwordVerification");
			add(new InputValidationBorder<NewUser>("validatedPasswordVerificationBorder", this, passwordVerificationInput));
 
			TextField<Integer> ageInput = new TextField<Integer>("age");
			add(new InputValidationBorder<NewUser>("validatedAgeBorder", this, ageInput));
 
			add(new Jsr303FormValidator(usernameInput, passwordInput, passwordVerificationInput, ageInput));
		}
 
		@Override
		protected void onSubmit() {
			// The NewUser model object is valid!
			// Perform your logic in here...
		}
 
	}
 
}

Now, there is a few things to explain in here:

  • The form has a NewUser bean associated. Its code will be shown in the next section.
  • The InputValidationBorder encapsulates the functionality to validate an input without validating the full bean and show the validation errors next to that input.
  • The Jsr303FormValidator is a form validator. It will only be called when its associated input components are valid (the email, passwords and age) and it will perform a bean scoped validation (in this case, it will check that the 2 supplied passwords are the same). In case it fails, the error will be reported in the panel’s feedback panel.
  • As the feedback panel should only report the errors that aren’t associated with a single input, its model is set so that only errors related to RegForm are reported. The only source of these messages will be the Jsr303FormValidator.

The bean to be validated

The form’s model is a NewUser bean. This bean encapsulates all the data that is requested to a new user. For every property in the bean some constraints must be enforced so they will be annotated following the JSR 303 standard. This is the resulting code NewUser.java:

@PasswordVerification
public class NewUser implements Serializable {
 
	// The email
	private String email;
 
	@NotNull
	@Email
	@Size(min=4,max=255)
	public String getEmail() {
		return this.email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
 
 
	// The password (uncyphered at this stage)
	private String password;
 
	@NotNull
	@Size(min=4)
	public String getPassword() {
		return this.password;
	}
 
	public void setPassword(String password) {
		this.password = password;
	}
 
 
	// The password verification
	private String passwordVerification;
 
	@NotNull
	public String getPasswordVerification() {
		return this.passwordVerification;
	}
 
	public void setPasswordVerification(String passwordVerification) {
		this.passwordVerification = passwordVerification;
	}
 
 
	// The age
	private Integer age;
 
	@NotNull
	@Max(140)
	@Min(18)
	public Integer getAge() {
		return this.age;
	}
 
	public void setAge(Integer age) {
		this.age = age;
	}
 
}

The PasswordVerification enforces that both supplied passwords match. It will be explained later. The Email annotation enforces a valid email address. It is a non-standard constraint part of hibernate validator, but you can easily code a replacement in case you are using a different validating engine and it doesn’t have that annotation.

The InputValidationBorder

This border component performs two functions:

  • It encapsulates the input scoped validation logic.
  • And it provides a way to show the related errors close to the input.

It is a generic class whose parameter T is the class of the form’s model object. Its code is as follows:

public class InputValidationBorder<T> extends Border {
 
	protected FeedbackPanel feedback;
 
	public InputValidationBorder(String id, final Form<T> form, final FormComponent<? extends Object> inputComponent) {
		super(id);
		add(inputComponent);
		inputComponent.setRequired(false);
		inputComponent.add(new AjaxFormComponentUpdatingBehavior("onblur") {
 
			@Override
			protected void onUpdate(AjaxRequestTarget target) {
				target.addComponent(InputValidationBorder.this.feedback);
			}
 
			@Override
			protected void onError(AjaxRequestTarget target, RuntimeException e) {
				target.addComponent(InputValidationBorder.this.feedback);
			}
 
		});
 
		inputComponent.add(new Jsr303PropertyValidator(form.getModelObject().getClass(), inputComponent.getId()));
 
		add(this.feedback = new FeedbackPanel("inputErrors", new ContainerFeedbackMessageFilter(this)));
		this.feedback.setOutputMarkupId(true);
	}
 
}

Again, a few things must be explained:

  • The input component is set to not-required. Bean validation will take care of that constraint in case the property is marked as @NotNull.
  • The added AjaxFormComponentUpdatingBehavior must override both onUpdate and onError. In both cases, when the methods are called the validation has already taken place. When the validation fails, onError is called, and the feedback component must be in the target to show the error messages. And when the validation succeeds, onUpdate is called, and the feedback component must again be in the target, so any older messages get cleared.
  • To save code, a convention where the same name for the input component id’s and their associated property in NewUser is used. Thats the reason the Jsr303PropertyValidator is instantiated with the inputComponent’s id.

The associated markup, InputValidationBorder.html is very simple. It just provides a placeholder for the feedback panel next to the input component:

<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd">  
<wicket:border>
    <wicket:body/>
    <span wicket:id="inputErrors"></span>
</wicket:border>
</html>

Jsr303PropertyValidator

This is a custom made validator that enforces the JSR 303 constraints on the indicated bean property. It implements INullAcceptingValidator (which extends IValidator) so also null values will be passed to the validator.

The validator instance is a Spring supplied bean. It is very easy to integrate Wicket with Spring. Just take care that if you must inject dependencies in something that is not a component, you will have to manually call the injector (as it is done in this validator). Also, in case you decide not to use Spring, you can easily change the code to obtain the validator from the Validation class.

The code of Jsr303PropertyValidator.java is as follows:

public class Jsr303PropertyValidator<T, Z> implements INullAcceptingValidator<T> {
 
	@SpringBean
	protected Validator validator;
 
	protected String propertyName;
	protected Class<Z> beanType;
 
	public Jsr303PropertyValidator(Class<Z> clazz, String propertyName) {
		this.propertyName = propertyName;
		this.beanType = clazz;
		injectDependencies();
	}
 
 
	private void injectDependencies() {
		InjectorHolder.getInjector().inject(this);
	}
 
 
	@Override
	public void validate(IValidatable<T> validatable) {
		Set<ConstraintViolation<Z>> res = this.validator.validateValue(this.beanType, this.propertyName, validatable.getValue());
		for ( ConstraintViolation<Z> vio : res ) {
			validatable.error(new ValidationError().setMessage(vio.getMessage()));
		}
	}
 
}

The class is generic: T is the class of the property to be validated, while Z is the class of the bean which contains the property (in this case, NewUser).

Jsr303FormValidator

This class implements IFormValidator, and it will be called when all the validations for the associated components have succeeded. It performs a full bean validation (not just the class level annotations), so you may use it to enforce individual properties as well. In this example, as all the properties’ constraints get previously validated via the Jsr303PropertyValidator, only the bean scoped constraints can fail.

This is the code of the class:

public class Jsr303FormValidator implements IFormValidator {
 
	@SpringBean
	protected Validator validator;
 
	private final FormComponent<?>[] components;
 
 
	public Jsr303FormValidator(FormComponent<?>...components) {
		this.components = components;
		injectDependencies();
	}
 
	private void injectDependencies() {
		InjectorHolder.getInjector().inject(this);
	}
 
 
	@Override
	public FormComponent<?>[] getDependentFormComponents() {
		return this.components;
	}
 
	@Override
	public void validate(Form<?> form) {
 
		ConstraintViolation[] res = this.validator.validate(form.getModelObject()).toArray(new ConstraintViolation[0]);
		for ( ConstraintViolation vio : res ) {
			form.error(new ValidationError().setMessage(vio.getMessage()));
		}
 
	}
 
}

The @PasswordVerification constraint

The NewUser bean is annotated with this constraint, that will enforce that the password and passwordVerification fields are the same. In order to work, it needs both the annotation definition code and the implementation of the validator. This is not really relevant to the integration part, but I provide it so there is a bean scoped constraint and you can check the Jsr303FormValidator. Here is the code for the annotation and the validator:

PasswordVerification.java

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordVerificationValidator.class)
public @interface PasswordVerification {
 
    String message() default "{newuser.passwordverification}";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
 
}

PasswordVerificationValidator.java

public class PasswordVerificationValidator implements ConstraintValidator<PasswordVerification, NewUser>{
 
	@Override
	public void initialize(PasswordVerification constraintAnnotation) {
		// Nothing to do
	}
 
	@Override
	public boolean isValid(NewUser value, ConstraintValidatorContext context) {
		if ( value.getPassword() == null && value.getPasswordVerification() == null ) {
			return true;
		}
		else if ( value.getPassword() == null ) {
			return false;
		}
		return ( value.getPassword().equals(value.getPasswordVerification()));
	}
 
}

Final touches, i18n

With the above code, you have all you need to use JSR 303 Validation in your Wicket forms. You have means to both validate individual properties associated with an input and the whole bean in a form’s model.

But the example is incomplete if you need your application to be available in various languages. The validation output messages are produced and interpolated by the validation engine, which isn’t aware of Wicket’s session locale. To correct this, a new MessageInterpolator which can access Wicket’s locale will be supplied to the validator bean.

The code of the new message interpolator (WicketSessionLocaleMessageInterpolator) is as follows:

import java.util.Locale;
import org.apache.wicket.Session;
import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
import org.springframework.stereotype.Component;
 
@Component(value="webLocaleInterpolator")
public class WicketSessionLocaleMessageInterpolator extends ResourceBundleMessageInterpolator {
 
	@Override
	public String interpolate(String message, Context context) {
		return super.interpolate(message, context, Session.get().getLocale());
	}
 
	@Override
	public String interpolate(String message, Context context, Locale locale) {
		return super.interpolate(message, context, Session.get().getLocale());
	}
 
}

This class extends ResourceBundleMessageInterpolator which is especific to Hibernate’s Validator implementation, but it’s very likely that if you use a different provider you can code a class similar to this one.

And the last needed step is to provide this bean to the validator declaration in Spring. This is the relevant part of the applicationContext.xml:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="messageInterpolator" ref="webLocaleInterpolator" />
</bean>

Now you have everything set-up: form and individual input validation with AJAX callbacks, and localized messages. Hope it can be of help :-)