CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-hibernate-validator--hibernate-validator

Hibernate Validator is the reference implementation of Jakarta Validation 3.1 providing annotation-based validation for JavaBeans and method parameters

Overview
Eval results
Files

message-interpolation.mddocs/

Message Interpolation

Advanced message interpolation with Expression Language support, configurable feature levels for security, custom locale resolution, resource bundle aggregation, and parameter-only interpolation for EL-free environments.

Capabilities

Resource Bundle Message Interpolator

Resource bundle-backed message interpolator with full Expression Language support.

package org.hibernate.validator.messageinterpolation;

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.Locale;
import java.util.Set;

/**
 * Resource bundle-backed message interpolator with Expression Language support.
 * Default message interpolator for Hibernate Validator.
 */
class ResourceBundleMessageInterpolator extends AbstractMessageInterpolator {
    /**
     * Create interpolator using default resource bundle ("ValidationMessages").
     */
    ResourceBundleMessageInterpolator();

    /**
     * Create interpolator with custom user resource bundle locator.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     */
    ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator);

    /**
     * Create interpolator with user and contributor resource bundle locators.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     */
    ResourceBundleMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator);

    /**
     * Create interpolator with user and contributor locators and caching control.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     * @param cachingEnabled whether to enable message caching
     */
    ResourceBundleMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator,
        boolean cachingEnabled);

    /**
     * Create interpolator with locale preloading support.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     * @param cachingEnabled whether to enable message caching
     * @param preloadResourceBundles whether to preload resource bundles for all locales
     * @since 6.1.1
     */
    @Incubating
    ResourceBundleMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator,
        boolean cachingEnabled,
        boolean preloadResourceBundles);

    /**
     * Create interpolator with locale support and preloading.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     * @param cachingEnabled whether to enable message caching
     * @param preloadResourceBundles whether to preload resource bundles
     * @param defaultLocale default locale for message interpolation
     * @param supportedLocales supported locales
     * @since 6.1.1
     */
    @Incubating
    ResourceBundleMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator,
        boolean cachingEnabled,
        boolean preloadResourceBundles,
        Locale defaultLocale,
        Set<Locale> supportedLocales);
}

Usage Example:

import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
import org.hibernate.validator.resourceloading.AggregateResourceBundleLocator;
import jakarta.validation.*;
import java.util.*;

// Create custom resource bundle locators
ResourceBundleLocator userLocator = new PlatformResourceBundleLocator("MyValidationMessages");
ResourceBundleLocator contributorLocator = new PlatformResourceBundleLocator("ContributorMessages");

// Create message interpolator with custom locators
ResourceBundleMessageInterpolator interpolator = new ResourceBundleMessageInterpolator(
    userLocator,
    contributorLocator,
    true,  // Enable caching
    true,  // Preload bundles
    Locale.US,
    Set.of(Locale.US, Locale.UK, Locale.FRANCE)
);

// Configure validator to use custom interpolator
ValidatorFactory factory = Validation.byDefaultProvider()
    .configure()
    .messageInterpolator(interpolator)
    .buildValidatorFactory();

Parameter Message Interpolator

Message interpolator without Expression Language support (parameter values only).

package org.hibernate.validator.messageinterpolation;

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.Locale;
import java.util.Set;

/**
 * Resource bundle message interpolator without EL support.
 * Only interpolates parameter values, providing better security
 * by avoiding expression evaluation. Use when EL features are not needed.
 *
 * @since 5.2
 */
class ParameterMessageInterpolator extends AbstractMessageInterpolator {
    /**
     * Create interpolator using default resource bundle.
     */
    ParameterMessageInterpolator();

    /**
     * Create interpolator with custom resource bundle locator.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     */
    ParameterMessageInterpolator(ResourceBundleLocator userResourceBundleLocator);

    /**
     * Create interpolator with user and contributor locators.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     */
    ParameterMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator);

    /**
     * Create interpolator with caching control.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     * @param cachingEnabled whether to enable message caching
     */
    ParameterMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator,
        boolean cachingEnabled);

    /**
     * Create interpolator with locale support.
     *
     * @param userResourceBundleLocator locator for user resource bundles
     * @param contributorResourceBundleLocator locator for contributor resource bundles
     * @param cachingEnabled whether to enable message caching
     * @param preloadResourceBundles whether to preload resource bundles
     * @param defaultLocale default locale
     * @param supportedLocales supported locales
     * @since 6.1.1
     */
    @Incubating
    ParameterMessageInterpolator(
        ResourceBundleLocator userResourceBundleLocator,
        ResourceBundleLocator contributorResourceBundleLocator,
        boolean cachingEnabled,
        boolean preloadResourceBundles,
        Locale defaultLocale,
        Set<Locale> supportedLocales);
}

Usage Example:

import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;

// Use parameter-only interpolator for security
ParameterMessageInterpolator interpolator = new ParameterMessageInterpolator();

ValidatorFactory factory = Validation.byDefaultProvider()
    .configure()
    .messageInterpolator(interpolator)
    .buildValidatorFactory();

// Now only parameter substitution works:
// "{min} <= value <= {max}" -> "0 <= value <= 100"
// EL expressions like "${formatter.format(...)}" will NOT be evaluated

Abstract Message Interpolator

Base class for message interpolators providing common functionality.

package org.hibernate.validator.messageinterpolation;

import jakarta.validation.MessageInterpolator;
import java.util.Locale;

/**
 * Base class for message interpolators.
 * Provides common functionality and template method for customization.
 */
abstract class AbstractMessageInterpolator implements MessageInterpolator {
    /**
     * Interpolate message with given context.
     *
     * @param message message template
     * @param context interpolation context
     * @return interpolated message
     */
    @Override
    String interpolate(String message, Context context);

    /**
     * Interpolate message with given context and locale.
     *
     * @param message message template
     * @param context interpolation context
     * @param locale locale for interpolation
     * @return interpolated message
     */
    @Override
    String interpolate(String message, Context context, Locale locale);
}

Expression Language Feature Level

Configure Expression Language features for security control.

package org.hibernate.validator.messageinterpolation;

/**
 * Expression Language feature level for controlling which EL features
 * are available for message interpolation. Provides security control
 * over EL expression evaluation.
 *
 * @since 6.2
 */
@Incubating
enum ExpressionLanguageFeatureLevel {
    /**
     * Context-dependent default level.
     * For constraints: resolves to BEAN_PROPERTIES (spec-compliant).
     * For custom violations: resolves to VARIABLES (safest for user input).
     */
    DEFAULT,

    /**
     * No EL interpolation.
     * Only parameter substitution (like {min}, {max}) is performed.
     * Safest option, equivalent to ParameterMessageInterpolator.
     */
    NONE,

    /**
     * Only injected variables, formatter, and ResourceBundles.
     * EL expressions can access:
     * - Variables added via addExpressionVariable()
     * - formatter object for formatting
     * - ResourceBundle messages
     * No access to bean properties or methods.
     */
    VARIABLES,

    /**
     * Variables plus bean properties (specification-compliant minimum).
     * EL expressions can access:
     * - All VARIABLES features
     * - Bean property getters (e.g., ${validatedValue.property})
     * No method execution allowed (except property getters).
     * This is the Jakarta Validation specification minimum.
     */
    BEAN_PROPERTIES,

    /**
     * Bean properties plus method execution (SECURITY RISK!).
     * EL expressions can access:
     * - All BEAN_PROPERTIES features
     * - Arbitrary method execution (e.g., ${object.method()})
     * WARNING: This is a security risk if user input can influence messages.
     * Only use in trusted environments.
     */
    BEAN_METHODS;

    /**
     * Parse feature level from string representation.
     *
     * @param expressionLanguageFeatureLevelString string representation
     * @return parsed feature level
     * @throws IllegalArgumentException if string is invalid
     */
    static ExpressionLanguageFeatureLevel of(String expressionLanguageFeatureLevelString);

    /**
     * Interpret DEFAULT for constraint messages.
     * Returns BEAN_PROPERTIES (spec-compliant) for DEFAULT, otherwise returns input.
     *
     * @param expressionLanguageFeatureLevel feature level to interpret
     * @return interpreted feature level
     */
    static ExpressionLanguageFeatureLevel interpretDefaultForConstraints(
        ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel);

    /**
     * Interpret DEFAULT for custom violation messages.
     * Returns VARIABLES (safest for user input) for DEFAULT, otherwise returns input.
     *
     * @param expressionLanguageFeatureLevel feature level to interpret
     * @return interpreted feature level
     */
    static ExpressionLanguageFeatureLevel interpretDefaultForCustomViolations(
        ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel);
}

Usage Example:

import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
import jakarta.validation.Validation;

// Configure EL feature level for security
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
    .configure()
    // Set EL level for static constraint messages
    .constraintExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.BEAN_PROPERTIES)
    // Set EL level for custom violations (more restrictive for user input)
    .customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES)
    .buildValidatorFactory();

// Feature level comparison:
// NONE:           "{min} <= value <= {max}" -> "0 <= value <= 100"
// VARIABLES:      "${myVar} is invalid" -> "custom value is invalid"
// BEAN_PROPERTIES: "${validatedValue.length()} chars" -> "5 chars"
// BEAN_METHODS:   "${validatedValue.toUpperCase()}" -> "HELLO" (SECURITY RISK!)

Hibernate Message Interpolator Context

Hibernate-specific extension to message interpolator context.

package org.hibernate.validator.messageinterpolation;

import jakarta.validation.MessageInterpolator;

/**
 * Hibernate-specific extension to MessageInterpolator.Context.
 * Provides additional context information for message interpolation.
 */
interface HibernateMessageInterpolatorContext extends MessageInterpolator.Context {
    /**
     * Get expression language variables added during validation.
     * Variables are added via HibernateConstraintValidatorContext.addExpressionVariable().
     *
     * @return map of expression language variables
     */
    java.util.Map<String, Object> getExpressionVariables();

    /**
     * Get the root bean being validated.
     *
     * @return root bean or null if not available
     */
    Object getRootBean();

    /**
     * Get message parameters added during validation.
     * Parameters are added via HibernateConstraintValidatorContext.addMessageParameter().
     *
     * @return map of message parameters
     */
    java.util.Map<String, Object> getMessageParameters();
}

Usage Example:

import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext;
import jakarta.validation.MessageInterpolator;
import jakarta.validation.metadata.ConstraintDescriptor;
import java.util.Locale;
import java.util.Map;

// Custom message interpolator using Hibernate context
class CustomMessageInterpolator implements MessageInterpolator {

    @Override
    public String interpolate(String messageTemplate, Context context) {
        return interpolate(messageTemplate, context, Locale.getDefault());
    }

    @Override
    public String interpolate(String messageTemplate, Context context, Locale locale) {
        // Unwrap to Hibernate context
        HibernateMessageInterpolatorContext hibernateContext =
            context.unwrap(HibernateMessageInterpolatorContext.class);

        // Access additional context information
        Map<String, Object> messageParams = hibernateContext.getMessageParameters();
        Map<String, Object> expressionVars = hibernateContext.getExpressionVariables();
        Object rootBean = hibernateContext.getRootBean();

        // Perform custom interpolation
        String message = messageTemplate;

        // Replace message parameters {paramName}
        for (Map.Entry<String, Object> entry : messageParams.entrySet()) {
            message = message.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue()));
        }

        // Process expression variables if needed
        // ...

        return message;
    }
}

Message Interpolation Examples

Basic Message Templates

// ValidationMessages.properties
javax.validation.constraints.NotNull.message=must not be null
javax.validation.constraints.Size.message=size must be between {min} and {max}
org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}

// Custom messages
user.email.invalid=Email address is invalid
user.age.range=Age must be between {min} and {max}, but was {value}

Expression Language in Messages

// Using formatter in message templates
@Size(min = 2, max = 10, message = "size is ${validatedValue.length()}, must be between {min} and {max}")
private String name;

// Using validated value
@Pattern(regexp = "[A-Z]+", message = "${validatedValue} must contain only uppercase letters")
private String code;

// Complex expressions with formatter
@DecimalMin(value = "0.0", message = "Value ${formatter.format('%1$.2f', validatedValue)} must be positive")
private BigDecimal amount;

Custom Message Parameters

import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import jakarta.validation.*;

class PriceRangeValidator implements ConstraintValidator<PriceRange, BigDecimal> {

    private BigDecimal min;
    private BigDecimal max;
    private String currency;

    @Override
    public void initialize(PriceRange constraintAnnotation) {
        this.min = constraintAnnotation.min();
        this.max = constraintAnnotation.max();
        this.currency = constraintAnnotation.currency();
    }

    @Override
    public boolean isValid(BigDecimal value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
            HibernateConstraintValidatorContext hibernateContext =
                context.unwrap(HibernateConstraintValidatorContext.class);

            context.disableDefaultConstraintViolation();

            hibernateContext
                .addMessageParameter("min", min)
                .addMessageParameter("max", max)
                .addMessageParameter("currency", currency)
                .addMessageParameter("value", value)
                .buildConstraintViolationWithTemplate(
                    "Price {value} {currency} is out of range [{min}, {max}] {currency}")
                .addConstraintViolation();

            return false;
        }

        return true;
    }
}

// Message output: "Price 150.00 USD is out of range [10.00, 100.00] USD"

Expression Language Variables

import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;

class StockValidator implements ConstraintValidator<InStock, Integer> {

    @Override
    public boolean isValid(Integer quantity, ConstraintValidatorContext context) {
        if (quantity == null) {
            return true;
        }

        int available = getAvailableStock();  // Get from inventory system

        if (quantity > available) {
            HibernateConstraintValidatorContext hibernateContext =
                context.unwrap(HibernateConstraintValidatorContext.class);

            context.disableDefaultConstraintViolation();

            hibernateContext
                .addExpressionVariable("requested", quantity)
                .addExpressionVariable("available", available)
                .addExpressionVariable("shortage", quantity - available)
                .buildConstraintViolationWithTemplate(
                    "Requested ${requested} items, only ${available} available (shortage: ${shortage})")
                .addConstraintViolation();

            return false;
        }

        return true;
    }

    private int getAvailableStock() {
        return 50;  // Example
    }
}

// Message output: "Requested 75 items, only 50 available (shortage: 25)"

Locale-Specific Messages

// ValidationMessages.properties (default English)
user.age.invalid=Age must be between {min} and {max}

// ValidationMessages_fr.properties (French)
user.age.invalid=L'âge doit être entre {min} et {max}

// ValidationMessages_de.properties (German)
user.age.invalid=Das Alter muss zwischen {min} und {max} liegen

// ValidationMessages_es.properties (Spanish)
user.age.invalid=La edad debe estar entre {min} y {max}

// Use with locale
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);

for (ConstraintViolation<User> violation : violations) {
    // Get message in different locales
    MessageInterpolator interpolator = factory.getMessageInterpolator();

    String englishMessage = interpolator.interpolate(
        violation.getMessageTemplate(),
        () -> violation.getConstraintDescriptor(),
        Locale.ENGLISH
    );

    String frenchMessage = interpolator.interpolate(
        violation.getMessageTemplate(),
        () -> violation.getConstraintDescriptor(),
        Locale.FRENCH
    );

    System.out.println("EN: " + englishMessage);
    System.out.println("FR: " + frenchMessage);
}

Install with Tessl CLI

npx tessl i tessl/maven-org-hibernate-validator--hibernate-validator

docs

configuration.md

constraint-validation.md

constraints.md

index.md

message-interpolation.md

path-navigation.md

programmatic-config.md

resource-loading.md

spi.md

tile.json