Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hibernate Validator: @ApplicationScoped custom ValueExtractor bean lead to ValueExtractorDefinitionException #20375

Closed
yrodiere opened this issue Sep 24, 2021 · 2 comments · Fixed by #20386
Assignees
Labels
area/hibernate-validator Hibernate Validator kind/bug Something isn't working
Milestone

Comments

@yrodiere
Copy link
Member

yrodiere commented Sep 24, 2021

Describe the bug

Even after we fix #20347, custom ValueExtractor implementations provided as CDI beans can only have the @Singleton scope or @Dependent pseudo-scope. Any scope requiring instantiation will lead to an exception on startup complaining about "parallel definitions of value extractors on a given class", even though that's not the case.

Expected behavior

A value extractor annotated with @ApplicationScoped should be correctly added to the ValidatorFactory, and be used automatically to constraints on extracted elements (e.g. Container<@NotBlank String>).

Actual behavior

Exception on startup:

java.lang.ExceptionInInitializerError
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at io.quarkus.runner.bootstrap.StartupActionImpl.run(StartupActionImpl.java:215)
        at io.quarkus.test.QuarkusUnitTest.beforeAll(QuarkusUnitTest.java:509)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$10(ClassBasedTestDescriptor.java:381)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:381)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:205)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:80)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
        at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
        at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
        at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:188)
        at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:154)
        at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:128)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:428)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
        at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:562)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:548)
Caused by: java.lang.RuntimeException: Failed to start quarkus
        at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:236)
        ... 46 more
Caused by: javax.validation.valueextraction.ValueExtractorDefinitionException: HV000218: Having parallel definitions of value extractors on a given class is not allowed: io.quarkus.hibernate.validator.test.valueextractor.ApplicationScopedCustomValueExtractorTest_ApplicationScopedContainerValueExtractor_ClientProxy.
        at org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor.getValueExtractorDefinition(ValueExtractorDescriptor.java:142)
        at org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor.<init>(ValueExtractorDescriptor.java:45)
        at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.addValueExtractor(AbstractConfigurationImpl.java:271)
        at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.addValueExtractor(AbstractConfigurationImpl.java:80)
        at io.quarkus.hibernate.validator.runtime.HibernateValidatorRecorder$2.created(HibernateValidatorRecorder.java:143)
        at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:70)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources-1025303321.deploy_0(ArcProcessor$generateResources-1025303321.zig:128)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources-1025303321.deploy(ArcProcessor$generateResources-1025303321.zig:40)
        at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:194)
        ... 46 more

How to Reproduce?

I'm going to add a disabled test as part of the PR for #20347. Look for ApplicationScopedCustomValueExtractorTest in the codebase.

Output of uname -a or ver

No response

Output of java -version

No response

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.3.0.CR1, f0aba85

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

I believe the cause is a bug in org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor#determineValueExtractorDefinitions. It gets passed the proxy class, which extends the actual implementation class. And for some reason, when passed a class that extends another class that implements ValueExtractor, it behaves as if ValueExtractor was implemented multiple times, both by the superclass and the subclass.

It seems caused by the fact that java.lang.Class#getAnnotatedInterfaces returns annotated interfaces of the class and its superclasses, which means determineValueExtractorDefinitions should not be walking through superclasses to collect all annotated interfaces (when it does, it collects duplicates).

@yrodiere yrodiere added the kind/bug Something isn't working label Sep 24, 2021
@quarkus-bot
Copy link

quarkus-bot bot commented Sep 24, 2021

/cc @gsmet

@yrodiere
Copy link
Member Author

Turns out the problem was in how we generate generics metadata for client proxies; see #20386.

geoand pushed a commit to geoand/quarkus that referenced this issue Sep 28, 2021
@quarkus-bot quarkus-bot bot added this to the 2.5 - main milestone Oct 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/hibernate-validator Hibernate Validator kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant