Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gunnarmorling/4168013 to your computer and use it in GitHub Desktop.
Save gunnarmorling/4168013 to your computer and use it in GitHub Desktop.

Hibernate Validator 5.0.0.Alpha2 and 4.3.1.Final

Today it's my pleasure to announce the simultaneous release of Hibernate Validator 5.0.0.Alpha2 and 4.3.1.Final.

Hibernate Validator 4.3

While we're heavily working on Hibernate Validator 5 in order to implement revision 1.1 of the Bean Validation spec, we decided to do a maintenance release of Hibernate Validator 4.3, the reference implementation of Bean Validation 1.0.

We fixed a couple of bugs, most notably HV-591. This caused an error when applying the @Email constraint to long strings. Interestingly, this is by far the most reported bug we've had so far in Hibernate Validator with altogether four duplicates. I'm wondering who choses an email address with over 60 characters, but we can validate it now :) Check out the change log for a complete overview of all issues fixed in 4.3.1.

Hibernate Validator 5

The 5.0.0.Alpha2 release catches up with the public review draft of Bean Validation 1.1. Let's have a closer look at some of the changes in the following.

Method validation

The methods for validating method arguments and return values have been moved from javax.validation.Validator to a new dedicated interface MethodValidator. It is obtained and used like this:

MethodValidator methodValidator = Validation
	.buildDefaultValidatorFactory()
	.getValidator()
	.forMethods();

OrderService myOrderService = ...;
Method placeOrderMethod = ...;
Object[] arguments = ...;

Set<ConstraintViolation<OrderService>> = methodValidator
	.validateParameters(myOrderService, placeOrderMethod, arguments);

Also related to method validation, there is now support for cross-parameter constraints. These come in handy when several parameters of a method or constructor need to be validated together, similar to traditional class-level constraints. The following shows an example:

@ConsistentDateParameters
public void createCalendarEvent(Date start, Date end) {
	//...
}

Opposed to constraints on individual parameters, cross-parameter constraints are specified on the method itself. To make it easy for readers of your code to differentiate cross-parameter constraints from return value constraints, it's recommended to choose a name which clearly expresses the constraint's purpose.

A cross-parameter constraint is defined like this:

@Target({ METHOD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@CrossParameterConstraint(validatedBy = ConsistentDateParametersValidator.class)
@Documented
public @interface ConsistentDateParameters {
	String message() default "{ConsistentDateParameters.message}";
	Class<?>[] groups() default { };
	Class<? extends Payload>[] payload() default { };
}

As you can see, this looks very similar to a normal Bean Validation constraint annotation. The only difference is that the @CrossParameterConstraint meta-annotation is used to specify the constraint's validator. The validator must support Object[] as validated type:

public class ConsistentDateParametersValidator implements ConstraintValidator<ConsistentDateParameters, Object[]> {

	@Override
	public void initialize(ConsistentDateParameters constraintAnnotation) {
		//nothing to do
	}

	@Override
	public boolean isValid(Object[] value, ConstraintValidatorContext context) {
		if ( value.length != 2 ) {
			throw new IllegalArgumentException( "Unexpected method signature" );
		}

		if ( value[0] == null || value[1] == null ) {
			return true;
		}

		if ( !( value[0] instanceof Date ) || !( value[1] instanceof Date ) ) {
			throw new IllegalArgumentException( "Unexpected method signature" );
		}

		return ( ( Date ) value[0] ).before( ( Date ) value[1] );
	}
}

As this validator operates on an array of objects representing the method arguments to be validated, it is required to check the number and types of arguments first. The advantage of this approach is that it allows to implement generic cross-parameter constraints which are not specific to one method:

@AreEqual(indexes={0,1}, message="Passwords must be identical")
public void resetPassword(String password, String confirmedPassword) {
	//...
}

The Bean Validation EG also considers a more type-safe approach, which has its own quirks, though. Let us know what would work best for you.

Integration with CDI

One of the big themes in Bean Validation 1.1 is the integration with CDI. The 5.0.0.Alpha2 release brings initial support for this. So it's possible now to have dependencies injected into constraint validator implementations via CDI:

public class OrderNumberValidator implements ConstraintValidator<ValidOrderNumber, String> {

	@Inject
	private OrderValidationService orderValidationService;

	@Override
	public void initialize(ValidOrderNumber constraintAnnotation) {
		//nothing to do
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {
		return orderValidationService.isValid(value);
	}

}

But that's not all. When invoking a method on a CDI managed bean which has parameter or return value constraints, automatically an interceptor kicks in, which validates the arguments and return value. If either is invalid, a ConstraintViolationException will be thrown. This protects the method implementation from illegal argument values and the caller from illegal return values. So you'll never have to write code like this again:

public void placeOrder(Item item, int quantity) {
	if(item == null) {
		throw new IllegalArgumentException("Item must not be null");
	}
	if(quantity < 1) {
		throw new IllegalArgumentException("Quantity must be at least 1");
	}
	
	//actual implementation
}

Instead this is sufficient:

public void placeOrder(@NotNull Item item, @Min(1) int quantity) {	
	//actual implementation
}

This does not only simplify the implementation, it also makes sure that the caller knows about the constraints, because they are part of the method's public API.

Group translation

Finally, there is support for the conversion of validation groups during cascaded validation, a long standing feature request with the Bean Validation spec. Remember, when annotating an element (e.g. a property or method parameter) with @Valid, validation will be propagated to the referenced object(s).

With help of the new ConvertGroup annotation it is now possible to define which group or sequence on the cascaded element shall be validated when a given group is validated on the parent. Let's look at an example:

//validation groups
public interface Complete extends Default {}
public interface BasicPostal {}
public interface FullPostal extends BasicPostal {}

public class Address {
	
	@NotNull(groups = BasicPostal.class)
	String street1;

	@NotNull
	String street2;

	@Size(groups = BasicPostal.class, min = 3)
	String zipCode = "12";

	@Size(groups = FullPostal.class, max = 2)
	String doorCode = "ABC";
}

public class User {

	@Valid
	@ConvertGroup.List({
		@ConvertGroup(from = Default.class, to = BasicPostal.class),
		@ConvertGroup(from = Complete.class, to = FullPostal.class)
	})
	Set<Address> addresses = new HashSet<>();
}

When validating a User object with the default group, for the associated addresses the constraints in the BasicPostal group will be validated. When validating a User object with the Complete group, this will lead to a validation of the constraints in the FullPostal group on the user's addresses.

This feature should help with cascaded validations throughout different layers of an application which all define different validation groups.

Separation of API, SPI and implementation

Not related to Bean Validation 1.1, the 5.0.0.Alpha2 release completes our works for a clear separation of API, SPI and implementation packages of the code base which we had begun in 4.3:

  • Internal: Types in the org.hibernate.validator.internal package are Hibernate Validator specific classes which are not intended for public use. Internal classes can change at any time and no guarantee is given regarding backwards compatibility.

  • SPI: Types within the org.hibernate.validator.spi package are service provider interfaces intended to be implemented or extended by users of Hibernate Validator.

  • API: Classes neither in the spi nor in the internal package are considered public API and can be used by users. If changes to this classes are necessary there will be a deprecation first.

While these are the major changes of the release, there are some more notable ones, e.g. minor modifications to the metadata API, some changes around ConfigurationSource as well as several bugfixes. The changelog has all the details for you.

Summing it up

This concludes our tour through the new Hibernate Validator releases.

As usual, both releases can be retrieved from SourceForge (4.3.1.Final, 5.0.0.Alpha2). The Maven coordinates are org.hibernate:hibernate-validator:4.3.1.Final respectively org.hibernate:hibernate-validator:5.0.0.Alpha2. Your feedback is welcome in the forum, the issue tracker and our mailing list. The new features are not described in the Hibernate Validator reference guide yet, so you might be interested in the latest specification draft.

If you have any ideas or proposals around the Bean Validation 1.1 spec, don't hesitate to engage directly with the expert group.

@hferentschik
Copy link

as a first comment I would also suggest to add the 4.3 stuff at the bottom as an "add-on". The main thing is really the 5.0.0.Beta2 release

@hferentschik
Copy link

The 5.0.0.Alpha2 release catches up with the public review draft of Bean Validation 1.1

I wold also mention the actual version number (1.1.0.Beta2). We use this a lot and just public review draft leaves it a little in the open I think

@hferentschik
Copy link

I would add links to BVAL issues where appropriate (or at least to the bigger changes - group conversion, CDI)

@hferentschik
Copy link

it's recommended to choose a name which clearly expresses the constraint's purpose.

is it? I would more put it as a tip

@hferentschik
Copy link

You could open the question regarding the getter validation. Not sure whether there is a jira issue or a beanvalidation.org page we can point to for a summary. I would like to hear what people thing about the idea of treating getters differently.

@hferentschik
Copy link

Finally, there is support for the conversion of validation groups...

Finally, there is support for the conversion of the validated groups...

@hferentschik
Copy link

Ok group translation has a link, nice

@hferentschik
Copy link

I think we described the package split before. If you want to shorten a little I would leave this paragraph out or maybe just in a single sentence say that the package refactoring is complete now ( including a link to the issue)

@hferentschik
Copy link

Personally I like to add links to issues by mentioning HV-xyz or BVAL-xyz directly. By linking actual text you keep me guessing where the links are going to and whether I want to follow them. Personal preference I guess

@hferentschik
Copy link

Overall awesome first post. Looking forward seeing it live

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment