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));