Skip to content

Commit b6a2535

Browse files
committed
Add CDI qualifier support for Concurrency 3.1 resource definitions
Register concurrency resources (ManagedExecutorService, ManagedScheduledExecutorService, ManagedThreadFactory, ContextService) as CDI beans with qualifier support per Concurrency 3.1 spec Section 5.4.1. - ConcurrencyCDIExtension: CDI extension that observes AfterBeanDiscovery and creates synthetic ApplicationScoped beans for resources with qualifiers. Also registers default beans (@Default/@Any) for all four concurrency resource types. - AnnotationDeployer: Extract qualifiers() from @ManagedExecutorDefinition, @ManagedScheduledExecutorDefinition, @ManagedThreadFactoryDefinition, @ContextServiceDefinition annotations into JEE model objects. - Convert*Definitions: Pass Qualifiers as comma-separated Resource property. - OptimizedLoaderService: Register ConcurrencyCDIExtension alongside JMS2CDIExtension. TCK Web profile: 196/196 passing (0 failures, 0 errors).
1 parent c8fa76f commit b6a2535

8 files changed

Lines changed: 758 additions & 0 deletions

File tree

container/openejb-core/src/main/java/org/apache/openejb/cdi/OptimizedLoaderService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ protected List<? extends Extension> loadExtensions(final ClassLoader classLoader
126126
list.add(new JMS2CDIExtension());
127127
}
128128

129+
list.add(new org.apache.openejb.cdi.concurrency.ConcurrencyCDIExtension());
130+
129131
final Collection<Extension> extensionCopy = new ArrayList<>(list);
130132

131133
final Iterator<Extension> it = list.iterator();

container/openejb-core/src/main/java/org/apache/openejb/cdi/concurrency/ConcurrencyCDIExtension.java

Lines changed: 530 additions & 0 deletions
Large diffs are not rendered by default.

container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,6 +4127,12 @@ private void buildContextServiceDefinition(final JndiConsumer consumer, final Co
41274127
contextService.getUnchanged().addAll(Arrays.asList(definition.unchanged()));
41284128
}
41294129

4130+
if (contextService.getQualifier().isEmpty() && definition.qualifiers().length > 0) {
4131+
for (final Class<?> qualifier : definition.qualifiers()) {
4132+
contextService.getQualifier().add(qualifier.getName());
4133+
}
4134+
}
4135+
41304136
consumer.getContextServiceMap().put(definition.name(), contextService);
41314137
}
41324138

@@ -4142,6 +4148,12 @@ private void buildManagedExecutorDefinition(final JndiConsumer consumer, final M
41424148
managedExecutor.setMaxAsync(definition.maxAsync() == -1 ? null : definition.maxAsync());
41434149
managedExecutor.setVirtual(definition.virtual() ? Boolean.TRUE : null);
41444150

4151+
if (managedExecutor.getQualifier().isEmpty() && definition.qualifiers().length > 0) {
4152+
for (final Class<?> qualifier : definition.qualifiers()) {
4153+
managedExecutor.getQualifier().add(qualifier.getName());
4154+
}
4155+
}
4156+
41454157
consumer.getManagedExecutorMap().put(definition.name(), managedExecutor);
41464158
}
41474159

@@ -4157,6 +4169,12 @@ private void buildManagedScheduledExecutorDefinition(final JndiConsumer consumer
41574169
managedScheduledExecutor.setMaxAsync(definition.maxAsync() == -1 ? null : definition.maxAsync());
41584170
managedScheduledExecutor.setVirtual(definition.virtual() ? Boolean.TRUE : null);
41594171

4172+
if (managedScheduledExecutor.getQualifier().isEmpty() && definition.qualifiers().length > 0) {
4173+
for (final Class<?> qualifier : definition.qualifiers()) {
4174+
managedScheduledExecutor.getQualifier().add(qualifier.getName());
4175+
}
4176+
}
4177+
41604178
consumer.getManagedScheduledExecutorMap().put(definition.name(), managedScheduledExecutor);
41614179
}
41624180

@@ -4171,6 +4189,12 @@ private void buildManagedThreadFactoryDefinition(final JndiConsumer consumer, Ma
41714189
managedThreadFactory.setPriority(definition.priority());
41724190
managedThreadFactory.setVirtual(definition.virtual() ? Boolean.TRUE : null);
41734191

4192+
if (managedThreadFactory.getQualifier().isEmpty() && definition.qualifiers().length > 0) {
4193+
for (final Class<?> qualifier : definition.qualifiers()) {
4194+
managedThreadFactory.getQualifier().add(qualifier.getName());
4195+
}
4196+
}
4197+
41744198
consumer.getManagedThreadFactoryMap().put(definition.name(), managedThreadFactory);
41754199
}
41764200

container/openejb-core/src/main/java/org/apache/openejb/config/ConvertContextServiceDefinitions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ private Resource toResource(final ContextService contextService) {
8888
put(p, "Propagated", Join.join(",", contextService.getPropagated()));
8989
put(p, "Cleared", Join.join(",", contextService.getCleared()));
9090
put(p, "Unchanged", Join.join(",", contextService.getUnchanged()));
91+
if (contextService.getQualifier() != null && !contextService.getQualifier().isEmpty()) {
92+
put(p, "Qualifiers", Join.join(",", contextService.getQualifier()));
93+
}
9194

9295
// to force it to be bound in JndiEncBuilder
9396
put(p, "JndiName", def.getJndi());

container/openejb-core/src/main/java/org/apache/openejb/config/ConvertManagedExecutorServiceDefinitions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.apache.openejb.jee.ManagedExecutor;
2424
import org.apache.openejb.util.PropertyPlaceHolderHelper;
2525

26+
import org.apache.openejb.util.Join;
27+
2628
import java.util.List;
2729
import java.util.Map;
2830
import java.util.Properties;
@@ -91,6 +93,9 @@ private Resource toResource(final ManagedExecutor managedExecutor) {
9193
put(p, "HungTaskThreshold", managedExecutor.getHungTaskThreshold());
9294
put(p, "Max", managedExecutor.getMaxAsync());
9395
put(p, "Virtual", managedExecutor.getVirtual());
96+
if (managedExecutor.getQualifier() != null && !managedExecutor.getQualifier().isEmpty()) {
97+
put(p, "Qualifiers", Join.join(",", managedExecutor.getQualifier()));
98+
}
9499

95100
// to force it to be bound in JndiEncBuilder
96101
put(p, "JndiName", def.getJndi());

container/openejb-core/src/main/java/org/apache/openejb/config/ConvertManagedScheduledExecutorServiceDefinitions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.apache.openejb.jee.ManagedScheduledExecutor;
2424
import org.apache.openejb.util.PropertyPlaceHolderHelper;
2525

26+
import org.apache.openejb.util.Join;
27+
2628
import java.util.List;
2729
import java.util.Map;
2830
import java.util.Properties;
@@ -91,6 +93,9 @@ private Resource toResource(final ManagedScheduledExecutor managedScheduledExecu
9193
put(p, "HungTaskThreshold", managedScheduledExecutor.getHungTaskThreshold());
9294
put(p, "Core", managedScheduledExecutor.getMaxAsync());
9395
put(p, "Virtual", managedScheduledExecutor.getVirtual());
96+
if (managedScheduledExecutor.getQualifier() != null && !managedScheduledExecutor.getQualifier().isEmpty()) {
97+
put(p, "Qualifiers", Join.join(",", managedScheduledExecutor.getQualifier()));
98+
}
9499

95100
// to force it to be bound in JndiEncBuilder
96101
put(p, "JndiName", def.getJndi());

container/openejb-core/src/main/java/org/apache/openejb/config/ConvertManagedThreadFactoryDefinitions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.apache.openejb.jee.ManagedThreadFactory;
2525
import org.apache.openejb.util.PropertyPlaceHolderHelper;
2626

27+
import org.apache.openejb.util.Join;
28+
2729
import java.util.List;
2830
import java.util.Map;
2931
import java.util.Properties;
@@ -90,6 +92,9 @@ private Resource toResource(final ManagedThreadFactory managedThreadFactory) {
9092
put(p, "Context", contextName);
9193
put(p, "Priority", managedThreadFactory.getPriority());
9294
put(p, "Virtual", managedThreadFactory.getVirtual());
95+
if (managedThreadFactory.getQualifier() != null && !managedThreadFactory.getQualifier().isEmpty()) {
96+
put(p, "Qualifiers", Join.join(",", managedThreadFactory.getQualifier()));
97+
}
9398

9499
// to force it to be bound in JndiEncBuilder
95100
put(p, "JndiName", def.getJndi());
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.openejb.cdi.concurrency;
18+
19+
import jakarta.enterprise.concurrent.ContextService;
20+
import jakarta.enterprise.concurrent.ManagedExecutorDefinition;
21+
import jakarta.enterprise.concurrent.ManagedExecutorService;
22+
import jakarta.enterprise.concurrent.ManagedScheduledExecutorDefinition;
23+
import jakarta.enterprise.concurrent.ManagedScheduledExecutorService;
24+
import jakarta.enterprise.concurrent.ManagedThreadFactory;
25+
import jakarta.enterprise.concurrent.ManagedThreadFactoryDefinition;
26+
import jakarta.enterprise.context.ApplicationScoped;
27+
import jakarta.enterprise.inject.Default;
28+
import jakarta.enterprise.util.Nonbinding;
29+
import jakarta.inject.Inject;
30+
import jakarta.inject.Qualifier;
31+
import org.apache.openejb.jee.EnterpriseBean;
32+
import org.apache.openejb.jee.SingletonBean;
33+
import org.apache.openejb.junit.ApplicationComposer;
34+
import org.apache.openejb.testing.Module;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
import java.lang.annotation.ElementType;
39+
import java.lang.annotation.Retention;
40+
import java.lang.annotation.RetentionPolicy;
41+
import java.lang.annotation.Target;
42+
import java.util.concurrent.CountDownLatch;
43+
import java.util.concurrent.TimeUnit;
44+
45+
import static org.junit.Assert.assertNotNull;
46+
import static org.junit.Assert.assertTrue;
47+
48+
/**
49+
* Verifies that the {@link ConcurrencyCDIExtension} correctly registers
50+
* concurrency resources as CDI beans, both with default and custom qualifiers.
51+
*/
52+
@RunWith(ApplicationComposer.class)
53+
public class ConcurrencyCDIExtensionTest {
54+
55+
@Inject
56+
private DefaultInjectionBean defaultBean;
57+
58+
@Inject
59+
private QualifiedInjectionBean qualifiedBean;
60+
61+
@Module
62+
public EnterpriseBean ejb() {
63+
return new SingletonBean(DummyEjb.class).localBean();
64+
}
65+
66+
@Module
67+
public Class<?>[] beans() {
68+
return new Class<?>[]{
69+
DefaultInjectionBean.class,
70+
QualifiedInjectionBean.class,
71+
AppConfig.class
72+
};
73+
}
74+
75+
@Test
76+
public void defaultManagedExecutorServiceIsInjectable() {
77+
assertNotNull("Default ManagedExecutorService should be injectable via @Inject",
78+
defaultBean.getMes());
79+
}
80+
81+
@Test
82+
public void defaultManagedScheduledExecutorServiceIsInjectable() {
83+
assertNotNull("Default ManagedScheduledExecutorService should be injectable via @Inject",
84+
defaultBean.getMses());
85+
}
86+
87+
@Test
88+
public void defaultManagedThreadFactoryIsInjectable() {
89+
assertNotNull("Default ManagedThreadFactory should be injectable via @Inject",
90+
defaultBean.getMtf());
91+
}
92+
93+
@Test
94+
public void defaultContextServiceIsInjectable() {
95+
assertNotNull("Default ContextService should be injectable via @Inject",
96+
defaultBean.getCs());
97+
}
98+
99+
@Test
100+
public void qualifiedManagedExecutorServiceIsInjectable() {
101+
assertNotNull("Qualified ManagedExecutorService should be injectable via @Inject @TestQualifier",
102+
qualifiedBean.getMes());
103+
}
104+
105+
@Test
106+
public void qualifiedManagedExecutorServiceExecutesTask() throws Exception {
107+
final CountDownLatch latch = new CountDownLatch(1);
108+
qualifiedBean.getMes().execute(latch::countDown);
109+
assertTrue("Task should complete on qualified MES",
110+
latch.await(5, TimeUnit.SECONDS));
111+
}
112+
113+
// --- Dummy EJB to trigger full resource deployment ---
114+
115+
@jakarta.ejb.Singleton
116+
public static class DummyEjb {
117+
}
118+
119+
// --- Qualifier ---
120+
121+
@Qualifier
122+
@Retention(RetentionPolicy.RUNTIME)
123+
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
124+
public @interface TestQualifier {
125+
}
126+
127+
// --- App config with qualifier-enabled definition ---
128+
129+
@ManagedExecutorDefinition(
130+
name = "java:comp/env/concurrent/TestQualifiedExecutor",
131+
qualifiers = {TestQualifier.class}
132+
)
133+
@ApplicationScoped
134+
public static class AppConfig {
135+
}
136+
137+
// --- Bean that injects default concurrency resources ---
138+
139+
@ApplicationScoped
140+
public static class DefaultInjectionBean {
141+
142+
@Inject
143+
private ManagedExecutorService mes;
144+
145+
@Inject
146+
private ManagedScheduledExecutorService mses;
147+
148+
@Inject
149+
private ManagedThreadFactory mtf;
150+
151+
@Inject
152+
private ContextService cs;
153+
154+
public ManagedExecutorService getMes() {
155+
return mes;
156+
}
157+
158+
public ManagedScheduledExecutorService getMses() {
159+
return mses;
160+
}
161+
162+
public ManagedThreadFactory getMtf() {
163+
return mtf;
164+
}
165+
166+
public ContextService getCs() {
167+
return cs;
168+
}
169+
}
170+
171+
// --- Bean that injects qualified concurrency resources ---
172+
173+
@ApplicationScoped
174+
public static class QualifiedInjectionBean {
175+
176+
@Inject
177+
@TestQualifier
178+
private ManagedExecutorService mes;
179+
180+
public ManagedExecutorService getMes() {
181+
return mes;
182+
}
183+
}
184+
}

0 commit comments

Comments
 (0)