Search code examples
javaexpressionmessagebean-validationhibernate-validator

Issue with message expressions in hibernate validator


I'm working on a project and I've been using Hibernate Validator for a while. Recently I tried to make use of Interpolation with message expressions (with EL features) in my validation messages, but I get inconsistent results (to my knowledge).

According to the documentation on message interpolation steps, message parameters (those enclosed in "{..}") are resolved prior to evaluation of message expressions (those enclosed in "${..}"). Hence, I conclude that it is allowed to write a message like this:

    ${validatedValue > someValue ? '{x.y.z.message}' : '{x.y.w.message}'}

and expect that a message literal whose key is either x.y.z.message or x.y.w.message be the result, where x.y.z.message and x.y.w.message are assumed to be valid and already defined message keys in a properly bootstrapped resource bundle.

To clarify things out, I've written a test case using TestNG and ran it against hibernate-validator-5.0.1.Final and hibernate-validator-5.1.0-Alpha. As is pointed out in test comments, the results obtained by these releases vary themselves on this specific issue which hints me towards an implementation bug. I first thought this might have to do with [HV-798] but it seems deeper.

Here is the test case:

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.constraints.Size;

import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class HibernateValidationTest
{
    public class Entity
    {
        public int  caseIndex;

        @Size(min = 4, max = 10, message = "${validatedValue.length() < min ? 'Error msg.#1' : 'Error msg.#2'}")
        public String getMessage1()
        {
            if (caseIndex == 0)
                return "abc";
            return "abcd";
        }

        @Size(min = 4, max = 10, message = "{javax.validation.constraints.Size.message}")
        public String getMessage2()
        {
            if (caseIndex == 1)
                return "abc";
            return "abcd";
        }

        @Size(min = 4, max = 10, message = "{javax.validation.constraints.Size.message} (${validatedValue.length() < min ? 'Error msg.#3' : '{javax.validation.constraints.Size.message}'})")
        public String getMessage3()
        {
            if (caseIndex == 2)
                return "abc";
            return "abcd";
        }
    }

    @Test
    public void testValidatorBug()
    {
        Validator validator = javax.validation.Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Entity>> violations;
        ConstraintViolation<Entity> violation;
        Entity entity = new Entity();

        /**
         * PASS
         */
        entity.caseIndex = 0;
        violations = validator.validate(entity);
        assertEquals(violations.size(), 1);
        violation = violations.iterator().next();
        assertEquals(violation.getMessage(), "Error msg.#1");

        /**
         * PASS
         */
        entity.caseIndex = 1;
        violations = validator.validate(entity);
        assertEquals(violations.size(), 1);
        violation = violations.iterator().next();
        assertEquals(violation.getMessage(), "size must be between 4 and 10");

        /**
         * FAIL
         * 
         * Violation message:
         * 
         * - on version hibernate-validator-5.0.1.Final: 
         *  size must be between 4 and 10 (${validatedValue.length() < min ? 'Error msg.#3' : '{javax.validation.constraints.Size.message}'})
         * 
         * - on hibernate-validator-5.1.0-20131114.015215-37: 
         *  {javax.validation.constraints.Size.message} (${validatedValue.length() < min ? 'Error msg.#3' : '{javax.validation.constraints.Size.message}'})
         */
        entity.caseIndex = 2;
        violations = validator.validate(entity);
        assertEquals(violations.size(), 1);
        violation = violations.iterator().next();
        assertEquals(violation.getMessage(), "size must be between 4 and 10 (Error msg.#3)");
    }
}

In this test Entity.getMessage3() demonstrates the issue which is caught by last part of the test. Based on my understanding, this message must be size must be between 4 and 10 (Error msg.#3), but it is size must be between 4 and 10 (${validatedValue.length() < min ? 'Error msg.#3' : '{javax.validation.constraints.Size.message}'}) on version 5.0.1 and {javax.validation.constraints.Size.message} (${validatedValue.length() < min ? 'Error msg.#3' : '{javax.validation.constraints.Size.message}'}) on 5.1.0.Alpha.

Can anyone kindly evaluate this and assert the issue or help me find my own mistake.

Thank you for your time.


Solution

  • After flipping around this I decided to follow @Gunnar recommendation and since we're using OSGi I managed to do a quick Bundle Class Path trick to have the modified ResourceBundleMessageInterpolator place before the original one which is automatically picked by HV.