Hibernate.orgCommunity Documentation
Message interpolation is the process of creating error messages for violated Bean Validation constraints. In this chapter you will learn how such messages are defined and resolved and how you can plug in custom message interpolators in case the default algorithm is not sufficient for your requirements.
Constraint violation messages are retrieved from so called message
descriptors. Each constraint defines its default message descriptor using
the message
attribute. At declaration time, the
default descriptor can be overridden with a specific value as shown in
Example 4.1, “Specifying a message descriptor using the
message attribute”.
Example 4.1. Specifying a message descriptor using the
message
attribute
package org.hibernate.validator.referenceguide.chapter04;
public class Car {
@NotNull(message = "The manufacturer name must not be null")
private String manufacturer;
//constructor, getters and setters ...
}
If a constraint is violated, its descriptor will be interpolated by
the validation engine using the currently configured
MessageInterpolator
. The interpolated error message
can then be retrieved from the resulting constraint violation by calling
ConstraintViolation#getMessage()
.
Message descriptors can contain message
parameters as well as message expressions
which will be resolved during interpolation. Message parameters are string
literals enclosed in {}
, while message expressions are
string literals enclosed in ${}
. The following
algorithm is applied during method interpolation:
Resolve any message parameters by using them as key for the
resource bundle ValidationMessages
. If this
bundle contains an entry for a given message parameter, that parameter
will be replaced in the message with the corresponding value from the
bundle. This step will be executed recursively in case the replaced
value again contains message parameters. The resource bundle is
expected to be provided by the application developer, e.g. by adding a
file named ValidationMessages.properties
to the
classpath. You can also create localized error messages by providing
locale specific variations of this bundle, such as
ValidationMessages_en_US.properties
. By default,
the JVM's default locale
(Locale#getDefault()
) will be used when
looking up messages in the bundle.
Resolve any message parameters by using them as key for a
resource bundle containing the standard error messages for the
built-in constraints as defined in Appendix B of the Bean Validation
specification. In the case of Hibernate Validator, this bundle is
named
org.hibernate.validator.ValidationMessages
. If
this step triggers a replacement, step 1 is executed again, otherwise
step 3 is applied.
Resolve any message parameters by replacing them with the value
of the constraint annotation member of the same name. This allows to
refer to attribute values of the constraint (e.g.
Size#min()
) in the error message (e.g. "must
be at least ${min}").
Resolve any message expressions by evaluating them as expressions of the Unified Expression Language. See Section 4.1.2, “Interpolation with message expressions” to learn more about the usage of Unified EL in error messages.
You can find the formal definition of the interpolation algorithm in section 5.3.1.1 of the Bean Validation specification.
Since the characters {
, }
and $
have a special meaning in message descriptors
they need to be escaped if you want to use them literally. The following
rules apply:
is considered as
the literal \{
{
\}
is considered as the literal
}
\$
is considered as the literal
$
\\
is considered as the literal
\
As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the Unified Expression Language (as defined by JSR 341) in constraint violation messages. This allows to define error messages based on conditional logic and also enables advanced formatting options. The validation engine makes the following objects available in the EL context:
the attribute values of the constraint mapped to the attribute names
the currently validated value (property, bean, method
parameter etc.) under the name
validatedValue
a bean mapped to the name formatter
exposing the var-arg method format(String format,
Object... args)
which behaves like
java.util.Formatter.format(String format, Object...
args)
.
The following section provides several examples for using EL expressions in error messages.
Example 4.2, “Specifying message descriptors” shows how to make use of the different options for specifying message descriptors.
Example 4.2. Specifying message descriptors
package org.hibernate.validator.referenceguide.chapter04.complete;
public class Car {
@NotNull
private String manufacturer;
@Size(
min = 2,
max = 14,
message = "The license plate '${validatedValue}' must be between {min} and {max} characters long"
)
private String licensePlate;
@Min(
value = 2,
message = "There must be at least {value} seat${value > 1 ? 's' : ''}"
)
private int seatCount;
@DecimalMax(
value = "350",
message = "The top speed ${formatter.format('%1$.2f', validatedValue)} is higher " +
"than {value}"
)
private double topSpeed;
@DecimalMax(value = "100000", message = "Price must not be higher than ${value}")
private BigDecimal price;
public Car(
String manufacturer,
String licensePlate,
int seatCount,
double topSpeed,
BigDecimal price) {
this.manufacturer = manufacturer;
this.licensePlate = licensePlate;
this.seatCount = seatCount;
this.topSpeed = topSpeed;
this.price = price;
}
//getters and setters ...
}
Validating an invalid Car
instance yields
constraint violations with the messages shown by the assertions in Example 4.3, “Expected error messages”:
the @NotNull
constraint on the
manufacturer
field causes the error message "may
not be null", as this is the default message defined by the Bean
Validation specification and no specific descriptor is given in the
message
attribute
the @Size
constraint on the
licensePlate
field shows the interpolation of
message parameters ({min}
,
{max}
) and how to add the validated value to the
error message using the EL expression ${validatedValue}
the @Min
constraint on
seatCount
demonstrates how use an EL expression
with a ternery expression to dynamically chose singular or plural
form, depending on an attribute of the constraint ("There must be at
least 1 seat" vs. "There must be at least 2 seats")
the message for the @DecimalMax
constraint on topSpeed
shows how to format the
validated value using the formatter
object
finally, the @DecimalMax
constraint on
price
shows that parameter interpolation has
precedence over expression evaluation, causing the
$
sign to show up in front of the maximum
price
Only actual constraint attributes can be interpolated using
message parameters in the form {attributeName}
. When
referring to the validated value or custom expression variables added
to the interpolation context (see Section 11.6.1, “HibernateConstraintValidatorContext”), an EL
expression in the form ${attributeName}
must be
used.
Example 4.3. Expected error messages
Car car = new Car( null, "A", 1, 400.123456, BigDecimal.valueOf( 200000 ) );
String message = validator.validateProperty( car, "manufacturer" )
.iterator()
.next()
.getMessage();
assertEquals( "may not be null", message );
message = validator.validateProperty( car, "licensePlate" )
.iterator()
.next()
.getMessage();
assertEquals(
"The license plate must be between 2 and 14 characters long",
message
);
message = validator.validateProperty( car, "seatCount" ).iterator().next().getMessage();
assertEquals( "There must be at least 2 seats", message );
message = validator.validateProperty( car, "topSpeed" ).iterator().next().getMessage();
assertEquals( "The top speed 400.12 is higher than 350", message );
message = validator.validateProperty( car, "price" ).iterator().next().getMessage();
assertEquals( "Price must not be higher than $100000", message );
If the default message interpolation algorithm does not fit your
requirements it is also possible to plug in a custom
MessageInterpolator
implementation.
Custom interpolators must implement the interface
javax.validation.MessageInterpolator
. Note that
implementations must be thread-safe. It is recommended that custom message
interpolators delegate final implementation to the default interpolator,
which can be obtained via
Configuration#getDefaultMessageInterpolator()
.
In order to use a custom message interpolator it must be registered
either by configuring it in the Bean Validation XML descriptor
META-INF/validation.xml
(see Section 7.1, “Configuring the validator factory in
validation.xml”) or by passing it when
bootstrapping a ValidatorFactory
or
Validator
(see Section 8.2.1, “MessageInterpolator” and Section 8.3, “Configuring a Validator”, respectively).
In some use cases you want to use the message interpolation
algorithm as defined by the Bean Validation specification, but retrieve
error messages from other resource bundles than
ValidationMessages
. In this situation Hibernate
Validator's ResourceBundleLocator
SPI can
help.
The default message interpolator in Hibernate Validator,
ResourceBundleMessageInterpolator
, delegates
retrieval of resource bundles to that SPI. Using an alternative bundle
only requires passing an instance of
PlatformResourceBundleLocator
with the bundle
name when bootstrapping the ValidatorFactory
as
shown in Example 4.4, “Using a specific resource bundle”.
Example 4.4. Using a specific resource bundle
Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator(
new ResourceBundleMessageInterpolator(
new PlatformResourceBundleLocator( "MyMessages" )
)
)
.buildValidatorFactory()
.getValidator();
Of course you also could implement a completely different
ResourceBundleLocator
, which for instance returns
bundles backed by records in a database. In this case you can obtain the
default locator via
HibernateValidatorConfiguration#getDefaultResourceBundleLocator()
,
which you e.g. could use as fallback for your custom locator.
Besides PlatformResourceBundleLocator
,
Hibernate Validator provides another resource bundle locator
implementation out of the box, namely
AggregateResourceBundleLocator
, which allows to
retrieve error messages from more than one resource bundle. You could
for instance use this implementation in a multi-module application where
you want to have one message bundle per module. Example 4.5, “Using
AggregateResourceBundleLocator” shows how to
use AggregateResourceBundleLocator
.
Example 4.5. Using
AggregateResourceBundleLocator
Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator(
new ResourceBundleMessageInterpolator(
new AggregateResourceBundleLocator(
Arrays.asList(
"MyMessages",
"MyOtherMessages"
)
)
)
)
.buildValidatorFactory()
.getValidator();
Note that the bundles are processed in the order as passed to the constructor. That means if several bundles contain an entry for a given message key, the value will be taken from the first bundle in the list containing the key.
Copyright © 2009 - 2013 Red Hat, Inc. & Gunnar Morling