9

According to the Glassfish 4.0 wiki, Glassfish 4.0 should include JSR349 Bean Validation 1.1.: GF4 wiki link

According to the JSR349 spec, CDI Injection should work out of the box: Bean Validation 1.1. CDI Integration

So I changed my pom.xml accordingly:

<dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
        <scope>provided</scope>
 </dependency>

And tried injecting a CDI Bean into the ConstraintValidator:

public class UniqueEmaiValidator implements ConstraintValidator<UniqueEmail, String> {

    @Inject
    private UserAccountService accountService;

    @Override
    public void initialize(UniqueEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
         return !accountService.userExistsByEmail(value);
    }
}

However, when testing the application (Running Arquillian 1.1.1. with arquillian-glassfish-remote-3.1 1.0.0.CR4), the validation will always fail because userAccountService is null and thus will throw a NullPointerException eventually.

What am I missing to make the Bean Validation 1.1 work?

edit:

A) Can confirm it is not caused by the Arquillian remote test - will also throw a NPEx. when run on the server

B) Running on a GlassFish Server Open Source Edition 4.0 (build 89)

C) I re-built the bean-validation.jar explicitly using the 5.0.1.FINAL of Hibernate Validator. The mvn package output:

[INFO] Building Validation API (JSR 349) version 1.1.0.Final, Hibernate Validator version 5.0.1.Final and its dependencies repackaged as OSGi bundle 2.1.92

Upon startup of the GlassFish server I get greeted with the following:

INFO:   GlassFish Server Open Source Edition  4.0  (89) startup time : Felix (5,736ms), startup services(2,078ms), total(7,814ms)
INFO:   HV000001: Hibernate Validator 5.0.1.Final

So I suppose the rebuilding did work. However, it did not resolve my issue of the NullPointerException :/

D) @Gunnar

This is the entity class, using a @Constraint annotation:

@Entity
public class UserAccount extends AbstractEntity implements VisibilitySettings {

  @UniqueEmail
  private String email;
  [...] 
}

The Annotation itself:

@Constraint(validatedBy = {UniqueEmailValidator.class})
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {

    String message() default "{validator.security.useraccount.emailexists}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

And the corresponding ConstraintValidator:

public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    @Inject
    private UserAccountService accountService;

    @Override
    public void initialize(UniqueEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return !accountService.userExistsByEmail(value);
    }
}

The UserAccountService is annotated with @ApplicationScoped @Transactional

chzbrgla
  • 5,158
  • 7
  • 39
  • 56
  • This should work and there are even TCK tests for ensuring that CDI injection works into ConstraintValidator instances. Is there anything else in the logs except the NullPointerException? Does CDI injection works outside Bean Validation? Rebuilding any Glassfish artifacts should not be necessary. It should work out of the box and Hibernate Validator 5.0.1.Final is the right version. – Hardy Nov 12 '13 at 13:24
  • 2
    How are you obtaining your `Validator`, by injecting it via `@Inject`? Note that CDI integration won't work out of the box when manually bootstrapping a validator via `Validation.buildDefaultValidatorFactory()`. – Gunnar Nov 12 '13 at 13:26
  • 1
    5.0.0.Final is fine as well. 5.0.1.Final is just a minor maintenance release. – Hardy Nov 12 '13 at 13:40
  • nvm @Gunnar - you're right. Some Boilerplate code actually did rework how I get a Validator. Will have a look into it. You could post that as an answer in the meantime - can't tick off comments :) – chzbrgla Nov 12 '13 at 14:16
  • Could you please tell us the way you rebuild glassfish4 bean-validator.jar with another version of hibernate-validator? – Markus Schulte May 13 '14 at 12:41

2 Answers2

3

CDI integration won't work out of the box when manually bootstrapping a validator via Validation.buildDefaultValidatorFactory().

When performing validation as part of the entity lifecycle via JPA, CDI integration should work; If it doesn't, there still might be an issue how Bean Validation is integrated into GlassFish. If this actually is the case, you might work around the issue by configuring a custom constraint validator factory in META-INF/validation.xml which creates CDI managed validator instances.

For this, you may use the factory provided by Hibernate Validator's CDI integration as starting point. Note that the factory needs a default constructor though when configured via XML; to satisfy that requirement you could get a reference to the BeanManager via JNDI.

Gunnar
  • 18,095
  • 1
  • 53
  • 73
  • Thank you for the answer. I investigated why we changed from ``@Inject Validator v`` to the Factory - turns out that guy who did it needed to set a ``MessageInterpolator`` on the ``Validator`` for i18n. How will this work when injecting through CDI? – chzbrgla Nov 12 '13 at 15:16
  • Ok. Got it - easy. Write my own ``MessageInterpolator`` implementation and put that into the ``WEB-INF/validation.xml``. – chzbrgla Nov 12 '13 at 15:32
  • @Gunhar I see you're one of the authors of the `InjectingConstraintValidatorFactory`. I'm having trouble getting it or bean validation in general working with RESTEasy in embedded Jetty. Would you mind taking a look at my question [here](http://stackoverflow.com/questions/21694596/resteasy-cdi-embedded-jetty-bean-validation-is-ignored)? – Abhijit Sarkar Feb 11 '14 at 06:53
3

I resolved. I created a Qualifier annotation:

@Qualifier   
@Retention(RUNTIME)   
@Target({TYPE,FIELD})   
public @interface Validation {

}

I annotated my ConstraintValidator:

@Validation 
public class MyValidatorImpl implements ConstraintValidator<MyValidator, MyBean> {

}

I created my ConstraintValidatorFactory:

public class CDIConstraintValidatorFactory implements ConstraintValidatorFactory {

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> type) {
        try{

            T t = getBeanInstance(type);
            if(t==null){
                t = type.newInstance();
            }
            return t;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    private BeanManager getBeanManager() throws NamingException {
            String name = "java:comp/" + BeanManager.class.getSimpleName();
            InitialContext ic = new InitialContext();
            BeanManager beanManager = (BeanManager) ic.lookup(name);
            return beanManager;
    }

    public <T> T getBeanInstance(final Class<T> type) throws Exception{
        BeanManager beanManager =  getBeanManager();
        Validation v = type.getAnnotation(Validation.class);
        if(v!=null){
            final Set<Bean<?>> beans = beanManager.getBeans(type,v);
            beanManager.resolve(beans);
            if(!beans.isEmpty()){
                final Bean<T> bean = (Bean<T>) beanManager.resolve(beans);
                final CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean);
                return (T) beanManager.getReference(bean, type,creationalContext);
            }else{
                return null;
            }
        }else{
            return null;
        }
    }



}

and then i registried it my validation.xml:

<?xml version="1.0" encoding="UTF-8"?>
<validation-config    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd">
        <constraint-validator-factory>xxx.be.framework.validator.CDIConstraintValidatorFactory</constraint-validator-factory>
</validation-config> 

regards, Giancarlo

  • It works for my CustomConstraintValidator implementaition with Injection but I got new error from other casual constraints without Inject-ed filed `java.lang.RuntimeException: javax.naming.NameNotFoundException: java:comp/BeanManager` – Michał Ziobro Jun 07 '15 at 21:46
  • this example works with jboss. which application servers are you using? – Giancarlo Compagno Nov 24 '15 at 16:48