h1. Context
* Method constraints validation * Kotlin ** Presence of extension function on parameterized type within the validated object
h1. Problem
Given the following code making use of method constraint validation
*SimpleFooService.kt*
{noformat}package foo
import org.slf4j.LoggerFactory import javax.validation.Validation import javax.validation.constraints.NotEmpty
class SimpleFooService { fun methodName(@NotEmpty ignored: String) { // no-op }
private fun List<String>.someExtensionFunction(extensionFunctionArg: Int) = Unit }
fun main() { val validator = Validation.buildDefaultValidatorFactory().validator val executableValidator = validator.forExecutables() try { executableValidator.validateParameters( SimpleFooService(), SimpleFooService::class.java.getDeclaredMethod(SimpleFooService::methodName.name, String::class.java), arrayOf("Some value") ) } catch (e: Exception) { log.error("Error", e) } }
val log = LoggerFactory.getLogger(SimpleFooService::class.java){noformat}
Attempting to execute the code leads to {{ArrayIndexOutOfBoundsException}} and the given arguments not being validated:
{noformat}2020-08-04 16:14:41,937 [main] DEBUG - org.jboss.logging - Logging Provider: org.jboss.logging.Slf4jLoggerProvider 2020-08-04 16:14:41,949 [main] INFO - org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 6.1.5.Final 2020-08-04 16:14:41,975 [main] DEBUG - org.hibernate.validator.internal.xml.config.ValidationXmlParser - Trying to load META-INF/validation.xml for XML based Validator configuration. 2020-08-04 16:14:41,977 [main] DEBUG - org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via TCCL 2020-08-04 16:14:41,979 [main] DEBUG - org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via Hibernate Validator's class loader 2020-08-04 16:14:41,980 [main] DEBUG - org.hibernate.validator.internal.xml.config.ValidationXmlParser - No META-INF/validation.xml found. Using annotation based configuration only. 2020-08-04 16:14:41,986 [main] DEBUG - org.hibernate.validator.internal.engine.resolver.TraversableResolvers - Cannot find javax.persistence.Persistence on classpath. Assuming non JPA 2 environment. All properties will per default be traversable. 2020-08-04 16:14:42,083 [main] DEBUG - org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator - Loaded expression factory via original TCCL 2020-08-04 16:14:42,434 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000252: Using org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider as property node name provider. 2020-08-04 16:14:42,443 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator as ValidatorFactory-scoped message interpolator. 2020-08-04 16:14:42,443 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.resolver.TraverseAllTraversableResolver as ValidatorFactory-scoped traversable resolver. 2020-08-04 16:14:42,443 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.util.ExecutableParameterNameProvider as ValidatorFactory-scoped parameter name provider. 2020-08-04 16:14:42,444 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.DefaultClockProvider as ValidatorFactory-scoped clock provider. 2020-08-04 16:14:42,444 [main] DEBUG - org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory as ValidatorFactory-scoped script evaluator factory. 2020-08-04 16:14:42,541 [main] ERROR - foo.SimpleFooService - Error java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getTypeParametersCascadingMetaDataForParameterizedType(AnnotationMetaDataProvider.java:678) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getTypeParametersCascadingMetadata(AnnotationMetaDataProvider.java:660) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findCascadingMetaData(AnnotationMetaDataProvider.java:627) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getParameterMetaData(AnnotationMetaDataProvider.java:432) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findExecutableMetaData(AnnotationMetaDataProvider.java:308) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getMetaData(AnnotationMetaDataProvider.java:292) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getMethodMetaData(AnnotationMetaDataProvider.java:279) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.retrieveBeanConfiguration(AnnotationMetaDataProvider.java:130) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getBeanConfiguration(AnnotationMetaDataProvider.java:120) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.getBeanConfigurationForHierarchy(BeanMetaDataManagerImpl.java:234) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.createBeanMetaData(BeanMetaDataManagerImpl.java:201) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.getBeanMetaData(BeanMetaDataManagerImpl.java:165) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:267) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:235) ~[hibernate-validator-6.1.5.Final.jar:6.1.5.Final] at foo.FooServiceKt.main(FooService.kt:19) ~[main/:na] at foo.FooServiceKt.main(FooService.kt) ~[main/:na]{noformat}
h1. Preliminary analysis
Debugging reveals that {{org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider#findExecutableMetaData}} [ is retrieving |https://github.com/hibernate/hibernate-validator/blob/6.1.5.Final/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java#L307] a incorrect {{JavaBeanExecutable}} result from {{JavaBeanHelper}} when processing the {{private final void foo.SimpleFooService.someExtensionFunction(java.util.List,int)}} method:
!image-2020-08-05-00-24-18-321.png|width=200,height=183!
(!) The {{extensionFunctionArg}} argument has the {{List<String>}} type associated to it, instead of {{int}}
That inconsistency causes {{AnnotationMetaDataProvider.getTypeParametersCascadingMetaDataForParameterizedType}} to fail.
h1. Workaround
Workaround is to remove all extension functions on parameterized receiver from the validated class (which may not be desirable).
h1. Build setup
*build.gradle.kts*
{ code:java noformat }import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
plugins { kotlin("jvm") version "1.3.72" }
group = "org.example" version = "1.0-SNAPSHOT"
repositories { mavenCentral() }
dependencies { implementation(kotlin("stdlib")) implementation(platform("org.springframework.boot:spring-boot-dependencies:2.3.2.RELEASE")) implementation("org.slf4j:slf4j-api") implementation("ch.qos.logback:logback-classic") implementation("org.hibernate.validator:hibernate-validator") implementation("org.glassfish:jakarta.el") }
tasks { "wrapper"(Wrapper::class) { gradleVersion = "6.5.1" distributionType = Wrapper.DistributionType.ALL } withType<KotlinCompile<KotlinJvmOptions>>().configureEach { kotlinOptions { jvmTarget = "11" javaParameters = true } } }{ code noformat } |
|