4

Anyone ever worked with spring-boot and run into this error.

javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator:
**<more stack...>**
Caused by: java.lang.NoSuchMethodException: 
com.comapany.package.validator.<init>()

I know it has something to do with this:

Any constraint implementations relying on ConstraintValidatorFactory behaviors specific to an implementation (dependency injection, no no-arg constructor and so on) are not considered portable.

but my Validator cannot be portable because it requires the repo to work so that is not an option.

I have tried adding a no-args constructor to my validator but then i get this:

javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
Caused by: java.lang.NullPointerException

I have also tried to override the LocalValidatorFactoryBean:

@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(ConstraintValidatorFactory validatorFactory) {
        var bean = new LocalValidatorFactoryBean();
        bean.setConstraintValidatorFactory(validatorFactory);
        return bean;
    }

and even though Spring recognizes my factory, I still get that error(HV000064).

Another option would be to add a concrete Repo in my no-args constructor for my validator, but that feels wrong.

public Validator(){
    repo = new ConcreteClassOfRepository();
}

So how do I proceed?

EDIT (reuested code):

ValidatorCode

@Service
@AllArgsConstructor
@NoArgsConstructor
public class UniqueIdValidator implements ConstraintValidator<UniqueId, String> {

    @Autowired
    private SampleRepository repo;


    @Override
    public void initialize(UniqueId constraintAnnotation) {

    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return !repo.existsById(s);
    }

}

StackTrace

javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.

    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:177)
    at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:68)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:127)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:120)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:552)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:515)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:485)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:447)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:397)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:173)
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:116)
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
    at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:211)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:84)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
    at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:348)
    at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:57)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108)
    at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1309)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1389)
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1558)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1526)
    at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1574)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.existsById(SimpleJpaRepository.java:327)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:371)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:204)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:657)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:621)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:605)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy112.existsById(Unknown Source)
    at com.packageName.ValidationTest.trackingBugCausedByValidatorNotPortable(ValidationTest.java:99)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.lang.NullPointerException
    at com.packageName.UniqueIdValidator.isValid(UniqueIdValidator.java:27)
    at com.packageName.UniqueIdValidator.isValid(UniqueIdValidator.java:11)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:171)

SampleRepository

@Repository
public interface SampleRepository extends JpaRepository<SampleEntity, String> {

}

Tracking Test

    @Test
    void trackingBugCausedByValidatorNotPortable() {
        var entity = new SampleEntity();
        entity.setId("an id");
        repo.save(entity);
        repo.delete(entity);
        assertTrue(repo.existsById("an id"));
        fail("Add @UniqueId to SampleEntity and run");
    }

repo.save() is successful, repo.delete() is successful, but repo.existsById() breaks with the aforementioned exception

CustomConstraintValidatorFactory & ImplementationFactory

public class CustomConstraintValidatorFactory implements ConstraintValidatorFactory {

    private final ConstraintValidatorFactory defaultFactory;
    private final ImplementationFactory[] implementations;

    public CustomConstraintValidatorFactory(ConstraintValidatorFactory defaultFactory, ImplementationFactory... implementations) {
        this.defaultFactory = defaultFactory;
        this.implementations = implementations;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        return Arrays.stream(implementations)
            .filter(factory -> factory.supports(key))
            .findFirst()
            .map(ImplementationFactory::create)
            .map(v -> (T) v)
            .orElseGet(() -> defaultFactory.getInstance(key));
    }

    @Override
    public void releaseInstance(ConstraintValidator<?, ?> instance) {
    }
}

public class InheritanceMatchValidatorFactory implements ImplementationFactory {

    Class klass;
    Supplier<ConstraintValidator> validator;

    @Override
    public boolean supports(Class key) {
        return klass.isAssignableFrom(key);
    }

    @Override
    public ConstraintValidator<?, ?> create() {
        return validator.get();
    }

}
kbluue
  • 369
  • 1
  • 5
  • 20

2 Answers2

0

can you try implementing SpringConstraintValidatorFactory in your test class and injecting application context also remove the @AllArgsConstructor @NoArgsConstructor before trying.

@Import({Validator.class})
@ContextConfiguration(classes = Application.class)
public class Test {
    private LocalValidatorFactoryBean validator;

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @BeforeEach
    void initialize() {
        SpringConstraintValidatorFactory springConstraintValidatorFactory
                = new SpringConstraintValidatorFactory(
                applicationContext.getAutowireCapableBeanFactory());
        validator = new LocalValidatorFactoryBean();
        validator.setConstraintValidatorFactory(springConstraintValidatorFactory);
        validator.setApplicationContext(applicationContext);
        validator.afterPropertiesSet();
    }
    @Test
    void testValidator(){
        //call the validator here
    }
}
Richard Elite
  • 720
  • 5
  • 15
  • I'm currently using `@SpringBootTest`, do I have to swap it out for `@ContextConfiguration(classes = MySpringApplicaton.class)`. Otherwise, `private ConfigurableApplicationContext applicationContext;` behaves exactly the same. – kbluue Jan 20 '20 at 18:40
0

man this question is a mess. Not explained what you're trying to do. You call method on repo, which does not exist there(did you mean CrudRepository?), unless you defined it on your own, and then it should have been shown. Entity is not shown. The actual issue is not pointed at ...

So it seems that your SampleRepository is not autowired. Most probably it is because your

public LocalValidatorFactoryBean localValidatorFactoryBean(ConstraintValidatorFactory validatorFactory)

just sets there a default ConstraintValidatorFactoryImpl class, which just creates validator instance using no-arg constructor via reflection and no injection takes place. Could you verify if it's not the case? If so, you need to find some strategy which does injection (is it SpringConstraintValidatorFactory maybe?) and set this one, ie. try to declare SpringConstraintValidatorFactory bean and use that bean to set LocalValidatorFactoryBean.

But it's just a guess, I don't understand this question sufficiently.

Martin Mucha
  • 2,385
  • 1
  • 29
  • 49
  • `@org.springframework.stereotype.Repository public interface SampleRepository extends Repository` `@Test void trackingBugCausedByValidatorNotPortable() { var entity = new SampleEntity(); entity.setId("an id"); repo.save(entity); repo.delete(entity); assertTrue(repo.existsById("something")); }` The error `javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.` is thrown in the `existsById()` line. I hope this helps. – kbluue Jan 21 '20 at 08:49
  • IIUC you got NPE there, and existsById does not throw NPE. So your repo is null there. Meaning that it's not injected. Which ConstraintValidatorFactory is injected into your config method named localValidatorFactoryBean? – Martin Mucha Jan 21 '20 at 09:37
  • Whichever I choose. I've tried injecting my own factory into `localValidatorFactoryBean` (see in question) and overriding the `ConstraintValidatorFactory` but neither works. I will update question with my tracking test and see if I can walk you through the process. – kbluue Jan 21 '20 at 09:59
  • so which one is used? What is injected in ConstraintValidatorFactory validatorFactory? I mean you set it as `bean.setConstraintValidatorFactory(validatorFactory);` but what is `validatorFactory`??? Instance of what class is it? – Martin Mucha Jan 21 '20 at 10:37
  • A custom built instance of `ConstraintValidatorFactory` – kbluue Jan 21 '20 at 11:51
  • great. So can you show us, how your custom built instance builds ConstraintValidator instances? – Martin Mucha Jan 21 '20 at 12:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206354/discussion-between-kbluue-and-martin-mucha). – kbluue Jan 21 '20 at 12:20