-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: JSR 303 Validator - Custom error message
PostPosted: Fri Mar 12, 2010 11:27 am 
Newbie

Joined: Tue Jan 26, 2010 3:20 pm
Posts: 6
I have been looking through the documentation and am uncertain as to what the correct way of defining a custom error message when creating a custom validator to the JSR 303 standard is

I have created the following interface
Code:
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = DateOfBirthIsValidValidator.class)
@Documented
@ReportAsSingleViolation
public @interface DateOfBirthIsValid {
   String message() default "A valid date of birth is required for all passengers";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

And the following implementation
Code:
public class DateOfBirthIsValidValidator implements ConstraintValidator<DateOfBirthIsValid, PassengerListItem>{
    @Override
    public void initialize(DateOfBirthIsValid dateOfBirthIsValid) {}
    @Override
    public boolean isValid(PassengerListItem passengerListItem, ConstraintValidatorContext constraintValidatorContext) {
   if (passengerListItem.dob is not valid) etc etc etc
   return false;
   }
}

What I would like to do is to create a custom error message if I am returning false the object I am validating has name information and I would like to return a custom string built from that data.

What is the correct way of doing this, I have played around with the ConstraintValidatorContext object calling the buildConstraintViolationWithTemplate() method and get no custom error message in the response object, just the default message as defined in the interface

I am in essence calling the validation into play using a javax.validation.ValidatorFactory to create a validation instance

I would appreciate a deeper understanding of what I should do if someone could provide it. I apologise if I have missed a previous posting asking the same question.

Thanks

Chris


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Sun Mar 14, 2010 1:45 pm 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Using the ConstraintValidatorContext is the right approach. How did your code look like? If you don't want the default error message. Something like this:
Code:
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext.buildConstraintViolationWithTemplate( "my error"  ).addConstraintViolation();

Make sure that you call addConstraintViolation - only after this call the new message/violation gets added.

--Hardy


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Mon Mar 15, 2010 10:15 am 
Newbie

Joined: Tue Jan 26, 2010 3:20 pm
Posts: 6
Hi Hardy

Thanks for the information, that was what I thought you were supposed to do and what I had tried unfortunately it does not seam to me to work.

in the Validator implementation I have this code

Code:
    @Override
    public boolean isValid(PassengerListItem pax, ConstraintValidatorContext constraintValidatorContext) {

       String paxName      = pax.getRPH() == null ? "" : pax.getRPH();
      String errorMessage = null;

       try {
          paxName = String.format("%s %s %s", pax.getName().getNamePrefix().getBodyText(), pax.getName().getGivenName().getBodyText(), pax.getName().getSurname().getBodyText());

       } catch (Exception e) {}

       Reason response = isValid(pax, paxName);

       switch (response) {

          case Valid:
             return true;

          case InvalidDOB:
             errorMessage = String .format("Passenger %s, requires a valid date of birth", paxName);
             break;

          case MaxAge:

              if (pax.isAdult())
                 errorMessage = String .format("Passenger %s, is to old to be classified as an adult for this holiday", paxName);

              else if (pax.isChild() || pax.isFreeChild())
                 errorMessage = String .format("Passenger %s, is to old to be classified as a child for this holiday", paxName);

              else if (pax.isInfant())
                 errorMessage = String .format("Passenger %s, is to old to be classified as a infant for this holiday", paxName);

              break;

          case MinAge:

              if (pax.isAdult())
                 errorMessage = String .format("Passenger %s, is to young to be classified as an adult for this holiday", paxName);

              else if (pax.isChild() || pax.isFreeChild())
                 errorMessage = String .format("Passenger %s, is to young to be classified as an child for this holiday", paxName);

              else if (pax.isInfant())
                 errorMessage = String .format("Passenger %s, is to young to be classified as an infant for this holiday", paxName);

              break;


       }

       if (errorMessage != null) {
          constraintValidatorContext.disableDefaultConstraintViolation();
          constraintValidatorContext.buildConstraintViolationWithTemplate( errorMessage ).addConstraintViolation();
       }
       return false;
    }


I call the validation code from a org.springframework.validation.Validator object which executes this code

Code:
  private ValidatorFactory   factory        = Validation.buildDefaultValidatorFactory();
  private Validator             validator      = null;

   /**
     * validate
     * @param obj - the object to validate
     * @param errors - the errors object to populate
     * @param classHierarchy - the class hierarchy to use to indicate to the
     */
    public void validate(Object obj, Errors errors, Class<?> classHierarchy) {
        if (obj == null)
            return;

        if (this.validator == null)
           this.validator = factory.getValidator();

        ArrayList<String> alreadyReported = new ArrayList<String>();

        Set<ConstraintViolation<Object>> constraintViolations = this.validator.validate(obj, classHierarchy);

        if (constraintViolations.isEmpty())
            return;

        for (ConstraintViolation<Object> constraintViolation : constraintViolations) {

           String errorCode = obj.getClass().getSimpleName() + "." +constraintViolation.getPropertyPath();

           errorCode = properties.getProperty(errorCode,errorCode);

            if (alreadyReported.contains(errorCode))
               continue;

            alreadyReported.add(errorCode);

            if (loggerIsDebug)
               logger.debug(String.format("Validation error code [%s] Message : [%s} ",errorCode,constraintViolation.getMessage()));

            errors.reject(errorCode, constraintViolation.getMessage());

        }
    }
 


I have stepped through the code, the error messages is updated to the context, it simply does not come out when I loop through the error instances, the default message appears instead

And ideas of where to look would be appreciated, I am using the 4.02 GA version of code if that helps any

Thank you

Chris March


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Mon Mar 15, 2010 3:19 pm 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

Your approach seems correct at least from a Validator point of view. I don't know anything about the Spring integration. Maybe you should post on the Spring forum as well.
Have you tried to write a unit test which tests your Validator independently from the Spring part?
I am also a little confused about the parameter name classHierarchy
Code:
this.validator.validate(obj, classHierarchy);

The second parameter is the validation groups and has nothing to do with a class hierarchy, but maybe that's just bad naming.

--Hardy


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Thu Mar 18, 2010 8:33 am 
Newbie

Joined: Tue Jan 26, 2010 3:20 pm
Posts: 6
Hi Hardy

I am as convinced as I can be that this is not doing as I think it should be and is a bug if you create these classes it will show the bug, I can send yo a zip of the classes to save you the time of creating the classes if you wish but I don't see a way of attaching a zip here

First the basic Annotation definition

Code:
package com.ttsltd.test;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;

@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = TestValidator.class)
@Documented
@ReportAsSingleViolation
public @interface IsValid {
   Class<?>[] groups() default {};
   String message() default "Default error message";
   Class<? extends Payload>[] payload() default {};
}


Now its implementation

Code:
package com.ttsltd.test;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class TestValidator implements ConstraintValidator<IsValid, DummyTestClass>{

   @Override
   public void initialize(IsValid isValid) {}

   @Override
   public boolean isValid(DummyTestClass dummyTestClass, ConstraintValidatorContext constraintValidatorContext) {
      constraintValidatorContext.disableDefaultConstraintViolation();
      constraintValidatorContext.buildConstraintViolationWithTemplate("Changed error message").addConstraintViolation();
      return false;
   }
}


This is a dummy class definition to operate on it has no fields as I could see no point in the test context

Code:
package com.ttsltd.test;
@IsValid(groups=TestChecks.class)
public class DummyTestClass {}


Now the interface definition to define the group that it should only run the test against

Code:
package com.ttsltd.test;
public interface TestChecks {}


And finally a junit test case

Code:
package com.ttsltd.test;

import static org.junit.Assert.*;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.junit.Test;
public class TestClass {
   @Test
   public void test() throws Exception {

       ValidatorFactory  factory           = Validation.buildDefaultValidatorFactory();
      Validator         validator         = factory.getValidator();
      DummyTestClass dummyTestClass = new DummyTestClass();

      Set<ConstraintViolation<DummyTestClass>> constraintViolations = validator.validate(dummyTestClass, TestChecks.class);

      for (ConstraintViolation<DummyTestClass> instance : constraintViolations)
         assertEquals("Changed error message", instance.getMessage());
   }
}


Basically my test case instantiates a new instance of DummyTestClass and runs the validation code against the instance, as the code testValidator instance is failing and altering the templae message I would expect the code to return the "Changed error message" it doesn't it returns the Deafult error message instead

I think this is incorrect see junit stack trace response below

Code:
org.junit.ComparisonFailure: expected:<[Changed] error message> but was:<[Default] error message>
   at org.junit.Assert.assertEquals(Assert.java:123)
   at org.junit.Assert.assertEquals(Assert.java:145)
   at com.ttsltd.test.TestClass.test(TestClass.java:34)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
   at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
   at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
   at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
   at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
   at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
   at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
   at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
   at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)



Can you let me have you thoughts

I appreciate your help by the way

Chris


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Thu Mar 18, 2010 8:36 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

I recommend you bundle everything up and attach it to a new Jira issue in HV


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Thu Mar 18, 2010 10:55 am 
Newbie

Joined: Tue Jan 26, 2010 3:20 pm
Posts: 6
I have created a jira to cover this the reference is http://opensource.atlassian.com/projects/hibernate/browse/HV-297


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Fri Mar 19, 2010 4:40 am 
Newbie

Joined: Tue Jan 26, 2010 3:20 pm
Posts: 6
Hi Hardy

Thanks for your help in locating the problem

Kind Regards

Chris


Top
 Profile  
 
 Post subject: Re: JSR 303 Validator - Custom error message
PostPosted: Fri Mar 19, 2010 7:47 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Thanks for reporting the bug :)
The problem is fixed on trunk now. A first beta release of Validator 4.1.0 will be out soon.

--Hardy


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.