Issue #3801 Jersey-Bean-Validator-fails-to-inject-Weld-managed-beans

Signed-off-by: David Matejcek <dmatej@seznam.cz>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index 3ccc26b..e473c17 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -2,6 +2,7 @@
 <!--
 
     Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2018, 2019 Payara Foundation and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -96,6 +97,16 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
 
         <!-- java-el related dependencies are in scope "provided" in hibernate-validator -->
         <dependency>
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/CompositeInjectingConstraintValidatorFactory.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/CompositeInjectingConstraintValidatorFactory.java
new file mode 100644
index 0000000..271015d
--- /dev/null
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/CompositeInjectingConstraintValidatorFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Payara Foundation  and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+package org.glassfish.jersey.server.validation.internal;
+
+import javax.annotation.PostConstruct;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+
+/**
+ * {@link ConstraintValidatorFactory} implementation that uses {@link InjectingConstraintValidatorFactory}
+ * by default and fallbacks to {@link HibernateInjectingConstraintValidatorFactory} when the resource
+ * cannot be found in resource context of Jersey.
+ *
+ * @author Mert Caliskan
+ */
+public class CompositeInjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
+
+    @Context
+    private ResourceContext resourceContext;
+
+    private InjectingConstraintValidatorFactory jerseyVF;
+    private HibernateInjectingConstraintValidatorFactory hibernateVF;
+
+    @PostConstruct
+    void postConstruct() {
+        jerseyVF = resourceContext.getResource(InjectingConstraintValidatorFactory.class);
+        hibernateVF = resourceContext.getResource(HibernateInjectingConstraintValidatorFactory.class);
+    }
+
+    @Override
+    public <T extends ConstraintValidator<?, ?>> T getInstance(final Class<T> key) {
+        T jerseyInstance = jerseyVF.getInstance(key);
+        if (jerseyInstance == null) {
+            return hibernateVF.getInstance(key);
+        }
+        return jerseyInstance;
+    }
+
+    @Override
+    public void releaseInstance(final ConstraintValidator<?, ?> instance) {
+        // NOOP
+    }
+}
\ No newline at end of file
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/DestructibleBeanInstance.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/DestructibleBeanInstance.java
new file mode 100644
index 0000000..136a007
--- /dev/null
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/DestructibleBeanInstance.java
@@ -0,0 +1,63 @@
+/*
+ * Hibernate Validator, declare and validate application constraints
+ *
+ * License: Apache License, Version 2.0
+ * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
+ */
+// Portions Copyright [2018] [Payara Foundation and/or its affiliates]
+
+package org.glassfish.jersey.server.validation.internal;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.InjectionTarget;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class DestructibleBeanInstance<T> {
+    private final T instance;
+    private final InjectionTarget<T> injectionTarget;
+
+    public DestructibleBeanInstance(BeanManager beanManager, Class<T> key) {
+        this.injectionTarget = createInjectionTarget(beanManager, key);
+        this.instance = createAndInjectBeans(beanManager, injectionTarget);
+    }
+
+    @SuppressWarnings("unchecked")
+    public DestructibleBeanInstance(BeanManager beanManager, T instance) {
+        this.injectionTarget = createInjectionTarget(beanManager, (Class<T>) instance.getClass());
+        injectBeans(beanManager, beanManager.createCreationalContext(null), injectionTarget, instance);
+        this.instance = instance;
+    }
+
+    public T getInstance() {
+        return instance;
+    }
+
+    public void destroy() {
+        injectionTarget.preDestroy(instance);
+        injectionTarget.dispose(instance);
+    }
+
+    private InjectionTarget<T> createInjectionTarget(BeanManager beanManager, Class<T> type) {
+        AnnotatedType<T> annotatedType = beanManager.createAnnotatedType(type);
+        return beanManager.createInjectionTarget(annotatedType);
+    }
+
+    private static <T> T createAndInjectBeans(BeanManager beanManager, InjectionTarget<T> injectionTarget) {
+        CreationalContext<T> creationalContext = beanManager.createCreationalContext(null);
+
+        T instance = injectionTarget.produce(creationalContext);
+        injectBeans(beanManager, creationalContext, injectionTarget, instance);
+
+        return instance;
+    }
+
+    private static <T> void injectBeans(BeanManager beanManager, CreationalContext<T> creationalContext,
+                                        InjectionTarget<T> injectionTarget, T instance) {
+        injectionTarget.inject(instance, creationalContext);
+        injectionTarget.postConstruct(instance);
+    }
+}
\ No newline at end of file
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/HibernateInjectingConstraintValidatorFactory.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/HibernateInjectingConstraintValidatorFactory.java
new file mode 100644
index 0000000..d0ad811
--- /dev/null
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/HibernateInjectingConstraintValidatorFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Hibernate Validator, declare and validate application constraints
+ *
+ * License: Apache License, Version 2.0
+ * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
+ */
+// Portions Copyright [2018] [Payara Foundation and/or its affiliates]
+
+package org.glassfish.jersey.server.validation.internal;
+
+import org.glassfish.jersey.ext.cdi1x.internal.CdiUtil;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class HibernateInjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
+    // TODO look for something with better performance (HF)
+    private final Map<Object, DestructibleBeanInstance<?>> constraintValidatorMap =
+            Collections.synchronizedMap(new IdentityHashMap<Object, DestructibleBeanInstance<?>>());
+
+    private BeanManager beanManager;
+
+    @PostConstruct
+    void postConstruct() {
+        this.beanManager = CdiUtil.getBeanManager();
+    }
+
+    @Override
+    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+        DestructibleBeanInstance<T> destructibleBeanInstance = new DestructibleBeanInstance<T>(beanManager, key);
+        constraintValidatorMap.put(destructibleBeanInstance.getInstance(), destructibleBeanInstance);
+        return destructibleBeanInstance.getInstance();
+    }
+
+    @Override
+    public void releaseInstance(ConstraintValidator<?, ?> instance) {
+        DestructibleBeanInstance<?> destructibleBeanInstance = constraintValidatorMap.remove(instance);
+        // HV-865 (Cleanup is multi threaded and instances can be removed by multiple threads.
+        // Explicit null check is needed)
+        if (destructibleBeanInstance != null) {
+            destructibleBeanInstance.destroy();
+        }
+    }
+}
+
+
+
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
index 050f920..a5b5d06 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -256,8 +257,9 @@
         private ValidatorContext getDefaultValidatorContext(final ValidateOnExecutionHandler handler) {
             final ValidatorContext context = factory.usingContext();
 
-            // Default Configuration.
-            context.constraintValidatorFactory(resourceContext.getResource(InjectingConstraintValidatorFactory.class));
+            // Composite Configuration - due to PAYARA-2491
+            // https://github.com/payara/Payara/issues/2245
+            context.constraintValidatorFactory(resourceContext.getResource(CompositeInjectingConstraintValidatorFactory.class));
 
             // Traversable Resolver.
             context.traversableResolver(getTraversableResolver(factory.getTraversableResolver(), handler));