Skip to content

Commit b752b3c

Browse files
committed
Allow SupplierClassBinding in ThreadScope in CDI
Signed-off-by: jansupol <[email protected]>
1 parent e254ae8 commit b752b3c

File tree

14 files changed

+554
-102
lines changed

14 files changed

+554
-102
lines changed

incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -129,13 +129,7 @@ public static <T> void registerSupplier(RuntimeType runtimeType, InitializableSu
129129
* CDI does not provide sufficient support for ThreadScoped Supplier
130130
*/
131131
if (binding.getScope() == PerThread.class) {
132-
BeanManagerImpl manager;
133-
if (beanManager instanceof BeanManagerProxy) {
134-
manager = ((BeanManagerProxy) beanManager).unwrap();
135-
} else {
136-
manager = (BeanManagerImpl) beanManager;
137-
}
138-
abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, manager));
132+
abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, beanManagerImpl(beanManager)));
139133
} else {
140134
abd.addBean(new InitializableSupplierInstanceBean<>(runtimeType, binding));
141135
abd.addBean(new InitializableSupplierInstanceBeanBridge<>(runtimeType, binding));
@@ -163,12 +157,18 @@ public static <T> BindingBeanPair registerSupplier(RuntimeType runtimeType, Supp
163157
InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
164158
supplierBean.setInjectionTarget(jit);
165159

166-
final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
167-
168-
abd.addBean(supplierBean);
169-
abd.addBean(supplierBeanBridge);
170-
171-
return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
160+
/*
161+
* CDI does not provide sufficient support for ThreadScoped Supplier
162+
*/
163+
if (binding.getScope() == PerThread.class) {
164+
abd.addBean(new SupplierThreadScopeClassBean(runtimeType, binding, supplierBean, beanManagerImpl(beanManager)));
165+
return null;
166+
} else {
167+
final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
168+
abd.addBean(supplierBean);
169+
abd.addBean(supplierBeanBridge);
170+
return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
171+
}
172172
}
173173

174174
/**
@@ -255,6 +255,14 @@ private static <T> ConstructorInjectionPoint<T> createConstructorInjectionPoint(
255255
return null;
256256
}
257257

258+
private static BeanManagerImpl beanManagerImpl(BeanManager beanManager) {
259+
if (beanManager instanceof BeanManagerProxy) {
260+
return ((BeanManagerProxy) beanManager).unwrap();
261+
} else {
262+
return (BeanManagerImpl) beanManager;
263+
}
264+
}
265+
258266
private static <T> InjectionTarget<T> getJerseyInjectionTarget(Class<T> clazz, InjectionTarget<T> injectionTarget,
259267
Bean<T> bean, Collection<InjectionResolver> resolvers) {
260268
BasicInjectionTarget<T> it = (BasicInjectionTarget<T>) injectionTarget;

incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -111,31 +111,4 @@ private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
111111
ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
112112
return factory.create(beanInstance);
113113
}
114-
115-
private static class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
116-
117-
private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
118-
119-
private final Supplier<T> supplier;
120-
121-
/**
122-
* Creates a new invocation handler with supplier which provides a current injected value in proper scope.
123-
*
124-
* @param supplier provider of the value.
125-
*/
126-
private ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
127-
super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
128-
this.supplier = supplier;
129-
}
130-
131-
@Override
132-
public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
133-
Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
134-
return super.invoke(instance, method, arguments);
135-
}
136-
137-
public void dispose() {
138-
this.instances.clear();
139-
}
140-
}
141114
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.inject.weld.internal.bean;
18+
19+
import org.glassfish.jersey.internal.inject.SupplierClassBinding;
20+
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
21+
import org.glassfish.jersey.internal.util.collection.LazyValue;
22+
import org.glassfish.jersey.internal.util.collection.Value;
23+
import org.glassfish.jersey.internal.util.collection.Values;
24+
import org.jboss.weld.bean.proxy.BeanInstance;
25+
import org.jboss.weld.bean.proxy.ProxyFactory;
26+
import org.jboss.weld.manager.BeanManagerImpl;
27+
28+
import javax.enterprise.context.Dependent;
29+
import javax.enterprise.context.spi.CreationalContext;
30+
import javax.ws.rs.RuntimeType;
31+
import java.lang.annotation.Annotation;
32+
import java.util.concurrent.atomic.AtomicReference;
33+
import java.util.function.Supplier;
34+
35+
36+
/**
37+
* Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}.
38+
* Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information.
39+
* The {@code Bean} does not use {@link org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget} because serves already
40+
* created proxy, therefore the create operation just return provided instance without any other contextual operation
41+
* (produce, inject, destroy).
42+
* <p>
43+
* This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and
44+
* works through the proxy which serves the correct instance per the given thread.
45+
* <p>
46+
* Register example:
47+
* <pre>
48+
* AbstractBinder {
49+
* &#64;Override
50+
* protected void configure() {
51+
* bindFactory(MyFactoryInjectionSupplier.class)
52+
* .to(MyBean.class)
53+
* .in(PerThread.class);
54+
* }
55+
* }
56+
* </pre>
57+
* Inject example:
58+
* <pre>
59+
* &#64;Path("/")
60+
* public class MyResource {
61+
* &#64;Inject
62+
* private MyBean myBean&#59;
63+
* }
64+
* </pre>
65+
*/
66+
class SupplierThreadScopeClassBean extends JerseyBean<Object> {
67+
private final LazyValue<ThreadScopeBeanInstance<Object>> beanInstance;
68+
private final SupplierClassBinding binding;
69+
private final LazyValue<Object> proxy;
70+
private final AtomicReference<CreationalContext> creationalContextAtomicReference = new AtomicReference<>();
71+
72+
SupplierThreadScopeClassBean(RuntimeType runtimeType,
73+
SupplierClassBinding binding,
74+
SupplierClassBean supplierClassBean,
75+
BeanManagerImpl beanManager) {
76+
super(runtimeType, binding);
77+
this.binding = binding;
78+
this.beanInstance = Values.lazy((Value<ThreadScopeBeanInstance<Object>>) () -> {
79+
Supplier supplierInstance = supplierClassBean.create(creationalContextAtomicReference.get());
80+
ThreadScopeBeanInstance scopeBeanInstance =
81+
new ThreadScopeBeanInstance(supplierInstance, this, beanManager.getContextId());
82+
return scopeBeanInstance;
83+
});
84+
this.proxy = Values.lazy((Value<Object>) () -> createClientProxy(beanInstance.get(), beanManager.getContextId()));
85+
}
86+
87+
@Override
88+
public Class<? extends Annotation> getScope() {
89+
return Dependent.class;
90+
}
91+
92+
@Override
93+
public Object create(CreationalContext<Object> ctx) {
94+
creationalContextAtomicReference.set(ctx);
95+
return proxy.get();
96+
}
97+
98+
@Override
99+
public void destroy(Object instance, CreationalContext<Object> creationalContext) {
100+
if (beanInstance.isInitialized()) {
101+
this.beanInstance.get().dispose();
102+
}
103+
}
104+
105+
@Override
106+
public Class<?> getBeanClass() {
107+
return (Class<?>) this.binding.getContracts().iterator().next();
108+
}
109+
110+
private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
111+
ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
112+
return factory.create(beanInstance);
113+
}
114+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.inject.weld.internal.bean;
18+
19+
import org.jboss.weld.bean.StringBeanIdentifier;
20+
import org.jboss.weld.bean.proxy.ContextBeanInstance;
21+
22+
import javax.enterprise.inject.spi.Bean;
23+
import javax.enterprise.inject.spi.PassivationCapable;
24+
import java.lang.reflect.Method;
25+
import java.util.WeakHashMap;
26+
import java.util.function.Supplier;
27+
28+
/**
29+
* {@link org.glassfish.jersey.internal.inject.PerThread} scope bean instance used from
30+
* {@link InitializableSupplierThreadScopeBean} and {@link SupplierThreadScopeClassBean}.
31+
*
32+
* @param <T> Typed of the bean supplied by a {@code Supplier}.
33+
*/
34+
class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
35+
36+
private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
37+
38+
private final Supplier<T> supplier;
39+
40+
/**
41+
* Creates a new invocation handler with supplier which provides a current injected value in proper scope.
42+
*
43+
* @param supplier provider of the value.
44+
*/
45+
ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
46+
super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
47+
this.supplier = supplier;
48+
}
49+
50+
@Override
51+
public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
52+
Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
53+
return super.invoke(instance, method, arguments);
54+
}
55+
56+
public void dispose() {
57+
this.instances.clear();
58+
}
59+
}

incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -347,8 +347,10 @@ private void registerBeans(RuntimeType runtimeType, CachingBinder binder, AfterB
347347
}
348348
BindingBeanPair pair = BeanHelper.registerSupplier(
349349
runtimeType, (SupplierClassBinding<?>) binding, abd, injectionResolvers, beanManager);
350-
for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
351-
supplierClassBindings.add(contract, pair);
350+
if (pair != null) {
351+
for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
352+
supplierClassBindings.add(contract, pair);
353+
}
352354
}
353355
} else if (InitializableInstanceBinding.class.isAssignableFrom(binding.getClass())) {
354356
if (RuntimeType.SERVER == runtimeType

incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,9 +26,11 @@ public class CzechGreeting implements Greeting, Printable {
2626

2727
static final String GREETING = "Ahoj";
2828

29+
private String greeting = GREETING + "#" + Thread.currentThread().getName();
30+
2931
@Override
3032
public String getGreeting() {
31-
return GREETING + "#" + Thread.currentThread().getName();
33+
return greeting;
3234
}
3335

3436
@Override

incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -97,6 +97,13 @@ public void register(RuntimeType runtimeType, AbstractBinder binder) {
9797
binder.bindFactory(new ThreadScopeTest.SupplierGreeting2(ThreadScopeTest.EnglishGreeting2.GREETING))
9898
.to(ThreadScopeTest.EnglishGreeting2.class)
9999
.in(PerThread.class);
100+
101+
//testSupplierClassBindingThreadScopedInSingletonScope
102+
binder.bindAsContract(ThreadScopeTest.SingletonObject3.class)
103+
.in(Singleton.class);
104+
binder.bindFactory(ThreadScopeTest.SupplierGreeting3.class)
105+
.to(ThreadScopeTest.Greeting3.class)
106+
.in(PerThread.class);
100107
}
101108

102109
//ClientInstanceInjectionTest

0 commit comments

Comments
 (0)