title: EJB injection fails in custom ConstraintValidator on JPA persist
body: <p>I have a problem with my JSF/JPA/Bean Validation app. Custom constraints annotating (JPA) entity fields don’t function correctly on JPA pre-persist although they work fine during JSF form validation</p>
  
  <p><strong>Environment:</strong>
  NetBeans IDE 8.0.2 / GlassFish Server 4.1
  JPA 2.1/EJB 3.2 /CDI 1.1/BeanValidation 1.1 /Hibernate Validator 5.0.0.Final
  cdi-api.jar added to ejb/web projects as it seems it was missing.</p>
  
  <p><strong>Problem</strong>
  My App contains a JSF 2.2 page whose backing/CDI bean (Register) contains references to fields of (JPA) entities on the server/EJB tier. The entities are annotated with constraint annotations, and during the form filling, the validation works fine. I think this is a fairly standard setup with a @Named(CDI) bean as the backing(controller) bean.</p>
  
  <p>I have custom constraints (@UserName and @Email) that check username and email is unique. Again these are EJB validators, and inject an EJB service which is used to check if username/email is in the DB using JPQL. Again this works fine when the form itself is being validated, and the form action will not be called if either of these validations fails.</p>
  
  <p>The problem starts when form validation is successful and the CDI bean makes the call to the ejb service to add the new entity.  In the log I see;</p>
  
  <pre><code>Caused by: javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
  ………………
  Caused by: java.lang.NullPointerException
      at com.nick.bookstore.validators.EmailValidator.isValid(EmailValidator.java:40)
  </code></pre>
  
  <p>(See attached logs)</p>
  
  <p>And I am able to see that the injected LoginService in the EmailValidator is null.
  EmailValidator uses LoginService:</p>
  
  <pre><code> @Inject 
   private LoginService loginService;
  </code></pre>
  
  <p>– so injection failed, and this causes a null pointer exception.</p>
  
  <p>So when validation is invoked from the CDI bean – it works, when managed by JPA it fails.</p>
  
  <p>I have seen a similar problem  <a href="https://stackoverflow.com/questions/20161837/custom-bean-validation-does-not-inject-cdi-beans-and-does-not-interpolate-mes">here</a> but this does not answer my question.</p>
  
  <p>I have written a JUnit test that invokes the EJB service to persist a entity(customer) directly and this confirms the error detailed above.</p>
  
  <p>Any help on understanding what is happening here is appreciated.</p>
  
  <p><strong>BookStoreProcessingBean(ejb)</strong></p>
  
  <pre><code>@Stateless
  @LocalBean
  
  public class BookStoreProcessingBean {
      @PersistenceContext(unitName = "BookStorePU")
      private EntityManager em;
  ….
   public void addCustomer(Customer customer) {
                          em.persist(customer);
       } 
  }  
  </code></pre>
  
  <p>Entities; <strong>Person</strong> -> Extended by <strong>Customer</strong></p>
  
  <pre><code>@Entity
  @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
  @DiscriminatorColumn(name="Type")
  public  class Person implements Serializable { 
  
      public Person(Address homeAddress) {
          this.homeAddress = homeAddress;
      }
  ……..    
     @Basic
     @Email
     @NotNull(message = "Email field must have a value")
     private String email;
  
  ……
  }
  
  @Entity
  public  class Customer extends Person implements Serializable {
  
      @Embedded
      private SecurityDetails securityDetails;
  …….
  }
  </code></pre>
  
  <p>Validators; <strong>@Email</strong> and <strong>@UserName</strong></p>
  
  <pre><code>public class EmailValidator implements ConstraintValidator&lt;Email, String&gt; {
  
      @Inject 
      private LoginService loginService;
  
  
      @Override
      public void initialize(Email constraintAnnotation) {
  
      }
  
      @Override
      public boolean isValid(String email, ConstraintValidatorContext context) {
          //First check for a valid pattern and then for existence in DB       
  ........         
         return !loginService.emailAlreadyExists(email); //Failure here as loginService is null
      }
  
  
  
  public class UserNameValidator implements ConstraintValidator&lt;UserName, String&gt; {
  
      @Inject
      private LoginService loginService;
  
      public UserNameValidator() {
      }
  
      @Override
      public void initialize(UserName constraintAnnotation)   
      }  
      @Override
      public boolean isValid(String userName, ConstraintValidatorContext context) {
          boolean unAlreadyExists = loginService.userNameAlreadyExists(userName);
          return !unAlreadyExists; 
      }
  </code></pre>
  
  <p><strong>Log Output</strong></p>
  
  <pre><code>    Warning:   A system exception occurred during an invocation on EJB BookStoreProcessingBean, method: public void com.nick.bookstore.ejb.BookStoreProcessingBean.addCustomer(com.nick.bookstore.entities.Customer)
  Warning:   javax.ejb.EJBException
      at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
      at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:698)
      at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:503)
      at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4566)
      at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2074)
      at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2044)
      at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:220)
      at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
      at com.sun.proxy.$Proxy305.addCustomer(Unknown Source)
      at com.nick.bookstore.ejb.__EJB31_Generated__BookStoreProcessingBean__Intf____Bean__.addCustomer(Unknown Source)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:414)
      at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:127)
      at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
      at org.jboss.weld.bean.proxy.InjectionPointPropagatingEnterpriseTargetBeanInstance.invoke(InjectionPointPropagatingEnterpriseTargetBeanInstance.java:65)
      at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:100)
      at com.nick.bookstore.ejb.BookStoreProcessingBean$Proxy$_$$_Weld$EnterpriseProxy$.addCustomer(Unknown Source)
      at com.nick.bookstore.beans.Register.register(Register.java:51)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at com.sun.el.parser.AstValue.invoke(AstValue.java:289)
      at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304)
      at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
      at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
      at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
      at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
      at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
      at javax.faces.component.UICommand.broadcast(UICommand.java:315)
      at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
      at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
      at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
      at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
      at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
      at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
      at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
      at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
      at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
      at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:415)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:282)
      at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
      at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
      at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
      at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
      at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
      at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
      at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
      at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
      at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
      at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
      at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
      at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
      at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
      at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
      at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
      at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
      at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
      at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
      at java.lang.Thread.run(Thread.java:745)
  Caused by: javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
      at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:284)
      at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:133)
      at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:91)
      at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:85)
      at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:478)
      at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:424)
      at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:388)
      at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:340)
      at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:158)
      at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.validateOnCallbackEvent(BeanValidationListener.java:84)
      at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.prePersist(BeanValidationListener.java:62)
      at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyListener(DescriptorEventManager.java:748)
      at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyEJB30Listeners(DescriptorEventManager.java:691)
      at org.eclipse.persistence.descriptors.DescriptorEventManager.executeEvent(DescriptorEventManager.java:229)
      at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectClone(UnitOfWorkImpl.java:4316)
      at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNotRegisteredNewObjectForPersist(UnitOfWorkImpl.java:4293)
      at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.registerNotRegisteredNewObjectForPersist(RepeatableWriteUnitOfWork.java:518)
      at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectForPersist(UnitOfWorkImpl.java:4235)
      at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:496)
      at com.sun.enterprise.container.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:287)
      at com.nick.bookstore.ejb.BookStoreProcessingBean.addCustomer(BookStoreProcessingBean.java:158)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
      at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
      at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4786)
      at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:656)
      at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
      at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:608)
      at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:46)
      at org.jboss.weld.ejb.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:52)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
      at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
      at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:608)
      at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doCall(SystemInterceptorProxy.java:163)
      at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:140)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
      at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
      at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
      at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4758)
      at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4746)
      at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:212)
      ... 60 more
  Caused by: java.lang.NullPointerException
      at com.nick.bookstore.validators.EmailValidator.isValid(EmailValidator.java:43)
      at com.nick.bookstore.validators.EmailValidator.isValid(EmailValidator.java:22)
      at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:281)
      ... 111 more
  </code></pre>