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

Injecting @ConfigMapping into ConstraintValidator results in an exception #43450

Open
marko-bekhta opened this issue Sep 23, 2024 · 7 comments
Open
Assignees
Labels
area/hibernate-validator Hibernate Validator kind/bug Something isn't working

Comments

@marko-bekhta
Copy link
Contributor

Describe the bug

Assume having some app properties as:

@ConfigMapping(prefix = "some.app.config.validation")
public interface ValidationConfigProperties {
    String pattern();
}

and then trying to use these properties in some custom constraint validator implementation as:

@ApplicationScoped
public class MyConstraintConstraintValidator implements ConstraintValidator<MyConstraint, String> {

    @Inject
    ValidationConfigProperties config;
    private Pattern pattern;

    @Override
    public void initialize(MyConstraint constraintAnnotation) {
        pattern = Pattern.compile(config.pattern());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        return pattern.matcher(value).matches();
    }
}

Expected behavior

ConstraintValidatorFactory correctly instantiates the MyConstraintConstraintValidator instance.

Actual behavior

An exception is thrown:

Caused by: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
	... 7 more
Caused by: jakarta.validation.ValidationException: HV000032: Unable to initialize io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_ClientProxy.
	at org.hibernate.validator.internal.engine.constraintvalidation.AbstractConstraintValidatorManagerImpl.initializeValidator(AbstractConstraintValidatorManagerImpl.java:148)
	at org.hibernate.validator.internal.engine.constraintvalidation.AbstractConstraintValidatorManagerImpl.createAndInitializeValidator(AbstractConstraintValidatorManagerImpl.java:90)
	at org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl.getInitializedValidator(PredefinedScopeConstraintValidatorManagerImpl.java:60)
	at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.<init>(ConstraintTree.java:56)
	at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.<init>(SimpleConstraintTree.java:37)
	at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.of(ConstraintTree.java:66)
	at org.hibernate.validator.internal.metadata.core.MetaConstraint.<init>(MetaConstraint.java:75)
	at org.hibernate.validator.internal.metadata.core.MetaConstraints.create(MetaConstraints.java:74)
	at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.convertToMetaConstraints(AnnotationMetaDataProvider.java:262)
	at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findPropertyMetaData(AnnotationMetaDataProvider.java:235)
	at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getFieldMetaData(AnnotationMetaDataProvider.java:229)
	at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.retrieveBeanConfiguration(AnnotationMetaDataProvider.java:130)
	at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getBeanConfiguration(AnnotationMetaDataProvider.java:121)
	at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.getBeanConfigurationForHierarchy(PredefinedScopeBeanMetaDataManager.java:183)
	at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.createBeanMetaData(PredefinedScopeBeanMetaDataManager.java:150)
	at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.<init>(PredefinedScopeBeanMetaDataManager.java:100)
	at org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl.<init>(PredefinedScopeValidatorFactoryImpl.java:206)
	at org.hibernate.validator.PredefinedScopeHibernateValidator.buildValidatorFactory(PredefinedScopeHibernateValidator.java:42)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:435)
	at io.quarkus.hibernate.validator.runtime.HibernateValidatorRecorder$2.created(HibernateValidatorRecorder.java:190)
	at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:80)
	at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy_0(Unknown Source)
	at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy(Unknown Source)
	... 8 more
Caused by: java.lang.RuntimeException: Error injecting io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator.config
	at io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_Bean.doCreate(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_Bean.create(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.c0(Unknown Source)
	at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.computeIfAbsent(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
	at io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_ClientProxy.arc$delegate(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.InjectedPropertyConstraintValidator_ClientProxy.initialize(Unknown Source)
	at org.hibernate.validator.internal.engine.constraintvalidation.AbstractConstraintValidatorManagerImpl.initializeValidator(AbstractConstraintValidatorManagerImpl.java:142)
	... 30 more
Caused by: jakarta.enterprise.inject.CreationException: Error creating synthetic bean [oli60TV5cswBFUOsKjucMk-Wsw8]: java.util.NoSuchElementException: SRCFG00027: Could not find a mapping for io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.doCreate(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.create(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.get(Unknown Source)
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.get(Unknown Source)
	at io.quarkus.arc.impl.CurrentInjectionPointProvider.get(CurrentInjectionPointProvider.java:48)
	... 43 more
Caused by: java.util.NoSuchElementException: SRCFG00027: Could not find a mapping for io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties
	at io.smallrye.config.SmallRyeConfig.getConfigMapping(SmallRyeConfig.java:592)
	at io.quarkus.arc.runtime.ConfigMappingCreator.create(ConfigMappingCreator.java:30)
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.createSynthetic(Unknown Source)
	... 48 more

How to Reproduce?

I've created a simple test in this commit marko-bekhta@f0fffb2

Output of uname -a or ver

Linux fedora 6.10.9-100.fc39.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Sep 9 02:28:01 UTC 2024 x86_64 GNU/Linux

Output of java -version

Java version: 21.0.4, vendor: Eclipse Adoptium

Quarkus version or git rev

3.14.3

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

Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)

Additional information

Injecting the same config properties elsewhere works fine.

@marko-bekhta marko-bekhta added kind/bug Something isn't working area/hibernate-validator Hibernate Validator labels Sep 23, 2024
Copy link

quarkus-bot bot commented Sep 23, 2024

/cc @radcortez (config)

@radcortez
Copy link
Member

You need to annotate the mapping with @StaticInitSafe because ConstraintValidators are initialized during STATIC_INIT:
https://quarkus.io/guides/config-mappings#static-init

@radcortez radcortez closed this as not planned Won't fix, can't repro, duplicate, stale Sep 23, 2024
@marko-bekhta
Copy link
Contributor Author

Ohh, sorry, @radcortez, I missed your comment and just noticed that you've closed the ticket.

Yeah, indeed, constraint validators are currently initialized during the static init. I opened this ticket since we are currently working towards a Hibernate Validator 9 release, and we have a chance to make some changes there (if we need to) to address some of the Quarkus use cases.

@StaticInitSafe doesn't fit the case when the user has to pass the application configuration at runtime, let's say, depending on where it is deployed.

Not sure what would be the best solution, but here are a few suggestions we can consider:

@yrodiere yrodiere reopened this Nov 19, 2024
@radcortez
Copy link
Member

There is already a way to use runtime configuration, by injecting a Instance, which is initialized at runtime:

@ApplicationScoped
public class MyConstraintConstraintValidator implements ConstraintValidator<MyConstraint, String> {
    @Inject
    Instance<ValidationConfigProperties> config;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        return config.get().pattern().matcher(value).matches();
    }
}

I think we shouldn't add specific APIs for this case, considering that other components have the same semantics (REST Providers for instance). This is not only for configuration but for any injectable bean.

@yrodiere
Copy link
Member

Thanks @radcortez! I think that's something we discussed with Marko last time we talked about this. I wonder if you tried @marko-bekhta?

If it works in the case of Validator, it looks like this would be the solution @marko-bekhta ?

come up with a workaround that would allow the user to use the runtime property and add an example of that to the docs ...

@yrodiere
Copy link
Member

By the way @radcortez , that's out of scope for this issue, but maybe we should create another one about clarifying the error message...

Caused by: java.util.NoSuchElementException: SRCFG00027: Could not find a mapping for io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties
	at io.smallrye.config.SmallRyeConfig.getConfigMapping(SmallRyeConfig.java:592)
	at io.quarkus.arc.runtime.ConfigMappingCreator.create(ConfigMappingCreator.java:30)
	at io.quarkus.it.hibernate.validator.injection.ValidationConfigProperties_oli60TV5cswBFUOsKjucMk-Wsw8_Synthetic_Bean.createSynthetic(Unknown Source)
	... 48 more

Shouldn't that mention something like "you're trying to use runtime config at static init" or even simply "no values found for property x or y"... ? So that the user understand what they are doing wrong.

Unless that information is in a cause you removed from the stack trace @marko-bekhta?

@radcortez
Copy link
Member

It is just a generic error message. The mapping is not available in the config, so it cannot be found.

We can certainly provide a more helpful message. We did something similar for values here: #36281

It should be possible to validate if the mapping injection can be done safely, and if not, warn the user.

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

No branches or pull requests

3 participants