javaee / jersey

This is no longer the active Jersey repository. Please see the README.md
http://jersey.github.io
Other
2.87k stars 2.36k forks source link

Cannot inject CDI bean into custom validator #3801

Open mhorejsi opened 6 years ago

mhorejsi commented 6 years ago

Jersey doesn't allow me to inject CDI bean into my custom Validator.

Sample code:

@ApplicationScoped
public class AllowedWeightsValidator implements ConstraintValidator<AllowedWeights, Map<String, Integer>> {

    @Inject
    private Localizer localizer;

    @Override
    public void initialize(AllowedWeights allowedWeights) {
        // ...
    }

    @Override
    public boolean isValid(Map<String, Integer> weights, ConstraintValidatorContext context) {
        // ...
    }

}

The exception is:

<org.glassfish.jersey.server.internal.JerseyResourceContext> <BEA-000000> <Lookup and initialization failed for a resource class: class package.AllowedWeightsValidator.
A MultiException has 1 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Localizer,parent=AllowedWeightsValidator,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1451781908)

        at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:75)
        at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:941)
        at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:980)
        at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1055)
        at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1046)
        at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:173)
        at org.glassfish.jersey.server.internal.JerseyResourceContext.getResource(JerseyResourceContext.java:102)
        at org.glassfish.jersey.server.validation.internal.InjectingConstraintValidatorFactory.getInstance(InjectingConstraintValidatorFactory.java:61)
        at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.createAndInitializeValidator(ConstraintValidatorManager.java:141)
        at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.getInitializedValidator(ConstraintValidatorManager.java:101)
        at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:125)
        at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:91)
        at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:83)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:547)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:487)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:451)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:403)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraint(ValidatorImpl.java:723)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraints(ValidatorImpl.java:601)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:412)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraint(ValidatorImpl.java:723)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateCascadedConstraints(ValidatorImpl.java:601)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateParametersInContext(ValidatorImpl.java:992)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:300)
        at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:254)
        at org.glassfish.jersey.server.validation.internal.DefaultConfiguredValidator.onValidate(DefaultConfiguredValidator.java:175)
        at org.glassfish.jersey.server.validation.internal.ValidationInterceptorExecutor.proceed(ValidationInterceptorExecutor.java:113)
        at org.glassfish.jersey.server.validation.internal.DefaultConfiguredValidator.validateResourceAndInputParams(DefaultConfiguredValidator.java:146)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:134)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
        at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)

Such injection should be according to Beans Validation spec (1.1, section 10.3.2) supported:

If no custom ConstraintValidatorFactory is requested by the user, the ValidatorFactory must be configured with a custom ConstraintValidatorFactory instance that returns CDI managed beans representing the requested ConstraintValidator types

When I inject (via @javax.inject.Inject) the Validator manually, everything works as expected. The problem occurs when I have a JAX-RS @POST method accepting POJO with my custom validation annotation.

This problem can be workarounded by registering the following ContextResolver:

public class ValidationConfigResolver implements ContextResolver<ValidationConfig> {

    @Context
    private ResourceContext resourceContext;

    @Override
    public ValidationConfig getContext(Class<?> aClass) {
        return new ValidationConfig().constraintValidatorFactory(new CDIConstraintValidatorFactory(resourceContext));
    }

    private static class CDIConstraintValidatorFactory implements ConstraintValidatorFactory {

        private final ResourceContext resourceContext;

        public CDIConstraintValidatorFactory(ResourceContext resourceContext) {
            this.resourceContext = resourceContext;
        }

        @Override
        public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> clazz) {
            Instance<T> cdiInstance = CDI.current().select(clazz);
            return cdiInstance.isUnsatisfied() ? resourceContext.getResource(clazz) : cdiInstance.get();
        }

        @Override
        public void releaseInstance(ConstraintValidator<?, ?> constraintValidator) {
            try {
                CDI.current().destroy(constraintValidator);
            } catch (UnsupportedOperationException ignored) {
                // If CDI cannot destroy the validator, that's perfectly fine.
                // It could have been created by HK2 (ResourceContext).
            }
        }
    }

}
mhorejsi commented 6 years ago

The affected Jersey version is 2.22.1.

jansupol commented 6 years ago

The injection has been significantly reworked in 2.26. Can you reproduce with latest Jersey?

mhorejsi commented 6 years ago

I use WebLogic server 12.2.1.2 which bundles Jersey 2.22.1. Once I have some spare time, I'll try to reproduce this issue with the latest Jersey.