Search code examples
javahibernatehibernate-validatorjakarta-validation

Validating class which cannot be modified by add new annotations


the model is generated by some tools. Particular fields are annotated by for instance @NotNull annotation. I cannot modify generated classes. I need to do additonal validation of generated classes like check that value of the string field does't contain empty string and then throw ConstraintViolationException. Currently I iterate over the class to find field type of String and then in case of empty string I need to implement my own ConstraintViolation interface which is deadly hard. So it looks like that:

    Validator validator = Validation.buildDefaultValidatorFactory()
            .getValidator();
    Set< ConstraintViolation< E > > validationErrors = validator.validate(aEntity);
    Field[] fields = aEntity.getClass().getDeclaredFields();
    for (Field f : fields) {
        Class<?> type = f.getType();
        if (type == String.class) {
            try {
                String v = (String) f.get(aEntity);
                // check that value is not an empty string
                if (v == null || !v.trim().isEmpty()) {
                    validationErrors.add(new ConstraintViolation<E>() {
                        @Override
                        public String getMessage() {
                            return null;
                        }

                        @Override
                        public String getMessageTemplate() {
                            return null;
                        }

                        @Override
                        public E getRootBean() {
                            return null;
                        }

                        @Override
                        public Class<E> getRootBeanClass() {
                            return null;
                        }

                        @Override
                        public Object getLeafBean() {
                            return null;
                        }

                        @Override
                        public Object[] getExecutableParameters() {
                            return new Object[0];
                        }

                        @Override
                        public Object getExecutableReturnValue() {
                            return null;
                        }

                        @Override
                        public Path getPropertyPath() {
                            return null;
                        }

                        @Override
                        public Object getInvalidValue() {
                            return null;
                        }

                        @Override
                        public ConstraintDescriptor<?> getConstraintDescriptor() {
                            return null;
                        }

                        @Override
                        public <U> U unwrap(Class<U> type) {
                            return null;
                        }
                    });
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

Is it possible to make this easier?


Solution

  • For cases when it is not possible to edit the classes to add validation annotations to it there are two options:

    Specifying the constraints via XML

    To use this approach you'd need to add a META-INF/validation.xml and then inside of it you can define/redefine your constraints:

    <?xml version="1.0" encoding="UTF-8"?>
    <constraint-mappings
        xmlns="https://jakarta.ee/xml/ns/validation/mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            https://jakarta.ee/xml/ns/validation/mapping
            https://jakarta.ee/xml/ns/validation/validation-mapping-3.0.xsd"
        version="3.0">
    
        <!-- note that you can use ignore-annotations 
            to either ignore any annotation constraints 
            or include them, depending on your needs -->
        <bean class="com.acme.common.model.SomeType" ignore-annotations="false">
           <field name="someField">
               <constraint annotation="jakarta.validation.constraints.NotBlank"/>
               [...]
           </field>
           [...]
        </bean>
    </constraint-mappings>
    

    for more details you should check this section of the specification.

    Programmatic constraint definition

    Alternatively, Hibernate Validator provides a way to add constraints programmatically:

    HibernateValidatorConfiguration configuration = Validation
            .byProvider( HibernateValidator.class )
            .configure();
    
    ConstraintMapping constraintMapping = configuration.createConstraintMapping();
    
    constraintMapping
        .type( Car.class )
            .field( "manufacturer" )
                .constraint( new NotNullDef() )
            .field( "licensePlate" )
                .ignoreAnnotations( true )
                .constraint( new NotNullDef() )
                .constraint( new SizeDef().min( 2 ).max( 14 ) )
        .type( RentalCar.class )
            .getter( "rentalStation" )
                .constraint( new NotNullDef() );
    
    Validator validator = configuration.addMapping( constraintMapping )
            .buildValidatorFactory()
            .getValidator();
    

    For more details, check this section of the Hibernate Validator documentation.

    If you do not know which fields will need not empty/blank constraint being applied you can try to iterate through the fields using the reflection and then based on that information define the constraints via programmatic API. once constraints are added to the validator you should be able to just use Validator#validate(..)