Skip to content

Commit 628b663

Browse files
committed
Add qualifier and virtual DD element support for Concurrency 3.1
- Add qualifier field (List<String>) to ContextService, ManagedExecutor, ManagedScheduledExecutor, ManagedThreadFactory DD model classes - Update SXC JAXB accessors to parse <qualifier> and <virtual> XML elements (virtual was in the model but missing from SXC parsers) - Fix NPE in Convert*Definitions when <context-service-ref> is absent in deployment descriptor — defaults to java:comp/DefaultContextService - Add unit tests for null context service fallback and qualifier model - Add Arquillian test deploying WAR with web.xml containing <virtual>
1 parent 23d5d24 commit 628b663

13 files changed

Lines changed: 405 additions & 7 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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.arquillian.tests.concurrency;
18+
19+
import jakarta.annotation.Resource;
20+
import jakarta.enterprise.concurrent.ManagedExecutorService;
21+
import jakarta.enterprise.concurrent.ManagedScheduledExecutorService;
22+
import jakarta.enterprise.concurrent.ManagedThreadFactory;
23+
import jakarta.enterprise.context.ApplicationScoped;
24+
import jakarta.inject.Inject;
25+
import org.jboss.arquillian.container.test.api.Deployment;
26+
import org.jboss.arquillian.junit.Arquillian;
27+
import org.jboss.shrinkwrap.api.ArchivePaths;
28+
import org.jboss.shrinkwrap.api.ShrinkWrap;
29+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
30+
import org.jboss.shrinkwrap.api.asset.StringAsset;
31+
import org.jboss.shrinkwrap.api.spec.WebArchive;
32+
import org.junit.Test;
33+
import org.junit.runner.RunWith;
34+
35+
import java.util.concurrent.CountDownLatch;
36+
import java.util.concurrent.TimeUnit;
37+
38+
import static org.junit.Assert.assertNotNull;
39+
import static org.junit.Assert.assertTrue;
40+
41+
/**
42+
* Arquillian test verifying that web.xml deployment descriptors with
43+
* {@code <virtual>} and {@code <qualifier>} elements deploy successfully.
44+
* This tests the SXC JAXB accessor parsing for Concurrency 3.1 DD elements.
45+
*/
46+
@RunWith(Arquillian.class)
47+
public class DeploymentDescriptorConcurrencyTest {
48+
49+
private static final String WEB_XML =
50+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
51+
"<web-app version=\"6.1\"\n" +
52+
" xmlns=\"https://jakarta.ee/xml/ns/jakartaee\"\n" +
53+
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
54+
" xsi:schemaLocation=\"https://jakarta.ee/xml/ns/jakartaee\n" +
55+
" https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd\">\n" +
56+
"\n" +
57+
" <managed-thread-factory>\n" +
58+
" <name>java:app/concurrent/DDThreadFactory</name>\n" +
59+
" <virtual>true</virtual>\n" +
60+
" </managed-thread-factory>\n" +
61+
"\n" +
62+
" <managed-executor>\n" +
63+
" <name>java:app/concurrent/DDExecutor</name>\n" +
64+
" <virtual>false</virtual>\n" +
65+
" </managed-executor>\n" +
66+
"\n" +
67+
" <managed-scheduled-executor>\n" +
68+
" <name>java:app/concurrent/DDScheduledExecutor</name>\n" +
69+
" <virtual>false</virtual>\n" +
70+
" </managed-scheduled-executor>\n" +
71+
"\n" +
72+
"</web-app>\n";
73+
74+
@Inject
75+
private DDBean ddBean;
76+
77+
@Deployment
78+
public static WebArchive createDeployment() {
79+
return ShrinkWrap.create(WebArchive.class, "DDConcurrencyTest.war")
80+
.addClasses(DDBean.class)
81+
.setWebXML(new StringAsset(WEB_XML))
82+
.addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
83+
}
84+
85+
@Test
86+
public void deploymentSucceeds() {
87+
// If we get here, the web.xml with <virtual> parsed successfully
88+
assertNotNull("DDBean should be injected", ddBean);
89+
}
90+
91+
@Test
92+
public void ddDefinedExecutorWorks() throws Exception {
93+
final boolean completed = ddBean.runOnDDExecutor();
94+
assertTrue("Task should run on DD-defined executor", completed);
95+
}
96+
97+
@ApplicationScoped
98+
public static class DDBean {
99+
100+
@Resource(lookup = "java:app/concurrent/DDExecutor")
101+
private ManagedExecutorService executor;
102+
103+
public boolean runOnDDExecutor() throws InterruptedException {
104+
final CountDownLatch latch = new CountDownLatch(1);
105+
executor.execute(latch::countDown);
106+
return latch.await(5, TimeUnit.SECONDS);
107+
}
108+
}
109+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ private Resource toResource(final ManagedExecutor managedExecutor) {
7878

7979
final Properties p = def.getProperties();
8080

81-
String contextName = managedExecutor.getContextService().getvalue();
81+
String contextName = managedExecutor.getContextService() != null
82+
? managedExecutor.getContextService().getvalue()
83+
: "java:comp/DefaultContextService";
8284
// Translate JNDI name to TomEE Resource ID, otherwise AutoConfig will fail to resolve it
8385
// and try to fix it by rewriting this to an unwanted ContextService
8486
if ("java:comp/DefaultContextService".equals(contextName)) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ private Resource toResource(final ManagedScheduledExecutor managedScheduledExecu
7777
def.setJndi(managedScheduledExecutor.getName().getvalue().replaceFirst("java:", ""));
7878

7979

80-
String contextName = managedScheduledExecutor.getContextService().getvalue();
80+
String contextName = managedScheduledExecutor.getContextService() != null
81+
? managedScheduledExecutor.getContextService().getvalue()
82+
: "java:comp/DefaultContextService";
8183
// Translate JNDI name to TomEE Resource ID, otherwise AutoConfig will fail to resolve it
8284
// and try to fix it by rewriting this to an unwanted ContextService
8385
if ("java:comp/DefaultContextService".equals(contextName)) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ private Resource toResource(final ManagedThreadFactory managedThreadFactory) {
7777

7878
def.setJndi(managedThreadFactory.getName().getvalue().replaceFirst("java:", ""));
7979

80-
String contextName = managedThreadFactory.getContextService().getvalue();
80+
String contextName = managedThreadFactory.getContextService() != null
81+
? managedThreadFactory.getContextService().getvalue()
82+
: "java:comp/DefaultContextService";
8183
// Translate JNDI name to TomEE Resource ID, otherwise AutoConfig will fail to resolve it
8284
// and try to fix it by rewriting this to an unwanted ContextService
8385
if ("java:comp/DefaultContextService".equals(contextName)) {

container/openejb-core/src/test/java/org/apache/openejb/config/ConvertVirtualDefinitionsTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,82 @@ public void scheduledExecutorVirtualTrue() throws OpenEJBException {
101101
assertEquals("true", resources.get(0).getProperties().getProperty("Virtual"));
102102
}
103103

104+
@Test
105+
public void threadFactoryWithNullContextServiceDefaults() throws OpenEJBException {
106+
final ManagedThreadFactory factory = new ManagedThreadFactory();
107+
factory.setName(jndi("java:comp/env/concurrent/NoCtxTF"));
108+
// contextService intentionally NOT set — should default to DefaultContextService
109+
factory.setPriority(5);
110+
111+
final AppModule appModule = createAppModuleWithThreadFactory(factory);
112+
new ConvertManagedThreadFactoryDefinitions().deploy(appModule);
113+
114+
final List<Resource> resources = new ArrayList<>(appModule.getResources());
115+
assertEquals(1, resources.size());
116+
assertEquals("Default Context Service",
117+
resources.get(0).getProperties().getProperty("Context"));
118+
}
119+
120+
@Test
121+
public void executorWithNullContextServiceDefaults() throws OpenEJBException {
122+
final ManagedExecutor executor = new ManagedExecutor();
123+
executor.setName(jndi("java:comp/env/concurrent/NoCtxMES"));
124+
// contextService intentionally NOT set
125+
126+
final AppModule appModule = createAppModuleWithExecutor(executor);
127+
new ConvertManagedExecutorServiceDefinitions().deploy(appModule);
128+
129+
final List<Resource> resources = new ArrayList<>(appModule.getResources());
130+
assertEquals(1, resources.size());
131+
assertEquals("Default Context Service",
132+
resources.get(0).getProperties().getProperty("Context"));
133+
}
134+
135+
@Test
136+
public void scheduledExecutorWithNullContextServiceDefaults() throws OpenEJBException {
137+
final ManagedScheduledExecutor executor = new ManagedScheduledExecutor();
138+
executor.setName(jndi("java:comp/env/concurrent/NoCtxMSES"));
139+
// contextService intentionally NOT set
140+
141+
final AppModule appModule = createAppModuleWithScheduledExecutor(executor);
142+
new ConvertManagedScheduledExecutorServiceDefinitions().deploy(appModule);
143+
144+
final List<Resource> resources = new ArrayList<>(appModule.getResources());
145+
assertEquals(1, resources.size());
146+
assertEquals("Default Context Service",
147+
resources.get(0).getProperties().getProperty("Context"));
148+
}
149+
150+
@Test
151+
public void threadFactoryQualifierIsPreserved() {
152+
final ManagedThreadFactory factory = new ManagedThreadFactory();
153+
factory.setName(jndi("java:comp/env/concurrent/QualifiedTF"));
154+
factory.setContextService(jndi("java:comp/DefaultContextService"));
155+
156+
final List<String> qualifiers = new ArrayList<>();
157+
qualifiers.add("com.example.MyQualifier");
158+
qualifiers.add("com.example.AnotherQualifier");
159+
factory.setQualifier(qualifiers);
160+
161+
assertEquals(2, factory.getQualifier().size());
162+
assertEquals("com.example.MyQualifier", factory.getQualifier().get(0));
163+
assertEquals("com.example.AnotherQualifier", factory.getQualifier().get(1));
164+
}
165+
166+
@Test
167+
public void executorQualifierIsPreserved() {
168+
final ManagedExecutor executor = new ManagedExecutor();
169+
executor.setName(jndi("java:comp/env/concurrent/QualifiedMES"));
170+
executor.setContextService(jndi("java:comp/DefaultContextService"));
171+
172+
final List<String> qualifiers = new ArrayList<>();
173+
qualifiers.add("com.example.ExecutorQualifier");
174+
executor.setQualifier(qualifiers);
175+
176+
assertEquals(1, executor.getQualifier().size());
177+
assertEquals("com.example.ExecutorQualifier", executor.getQualifier().get(0));
178+
}
179+
104180
// --- helpers ---
105181

106182
private static JndiName jndi(final String value) {

container/openejb-jee-accessors/src/main/java/org/apache/openejb/jee/ContextService$JAXB.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public static final ContextService _read(XoXMLStreamReader reader, RuntimeContex
8888
List<String> cleared = null;
8989
List<String> propagated = null;
9090
List<String> unchanged = null;
91+
List<String> qualifier = null;
9192
List<Property> property = null;
9293

9394
// Check xsi:type
@@ -183,6 +184,18 @@ public static final ContextService _read(XoXMLStreamReader reader, RuntimeContex
183184
}
184185
}
185186
unchanged.add(unchangedItem);
187+
} else if (("qualifier" == elementReader.getLocalName())&&("http://java.sun.com/xml/ns/javaee" == elementReader.getNamespaceURI())) {
188+
// ELEMENT: qualifier
189+
String qualifierItem = elementReader.getElementText();
190+
if (qualifier == null) {
191+
qualifier = contextService.qualifier;
192+
if (qualifier!= null) {
193+
qualifier.clear();
194+
} else {
195+
qualifier = new ArrayList<>();
196+
}
197+
}
198+
qualifier.add(qualifierItem);
186199
} else if (("property" == elementReader.getLocalName())&&("http://java.sun.com/xml/ns/javaee" == elementReader.getNamespaceURI())) {
187200
// ELEMENT: property
188201
Property propertyItem = readProperty(elementReader, context);
@@ -196,7 +209,7 @@ public static final ContextService _read(XoXMLStreamReader reader, RuntimeContex
196209
}
197210
property.add(propertyItem);
198211
} else {
199-
context.unexpectedElement(elementReader, new QName("http://java.sun.com/xml/ns/javaee", "description"), new QName("http://java.sun.com/xml/ns/javaee", "name"), new QName("http://java.sun.com/xml/ns/javaee", "cleared"), new QName("http://java.sun.com/xml/ns/javaee", "propagated"), new QName("http://java.sun.com/xml/ns/javaee", "unchanged"), new QName("http://java.sun.com/xml/ns/javaee", "property"));
212+
context.unexpectedElement(elementReader, new QName("http://java.sun.com/xml/ns/javaee", "description"), new QName("http://java.sun.com/xml/ns/javaee", "name"), new QName("http://java.sun.com/xml/ns/javaee", "cleared"), new QName("http://java.sun.com/xml/ns/javaee", "propagated"), new QName("http://java.sun.com/xml/ns/javaee", "unchanged"), new QName("http://java.sun.com/xml/ns/javaee", "qualifier"), new QName("http://java.sun.com/xml/ns/javaee", "property"));
200213
}
201214
}
202215
if (cleared!= null) {
@@ -208,6 +221,9 @@ public static final ContextService _read(XoXMLStreamReader reader, RuntimeContex
208221
if (unchanged!= null) {
209222
contextService.unchanged = unchanged;
210223
}
224+
if (qualifier!= null) {
225+
contextService.qualifier = qualifier;
226+
}
211227
if (property!= null) {
212228
contextService.property = property;
213229
}
@@ -328,6 +344,18 @@ public static final void _write(XoXMLStreamWriter writer, ContextService context
328344
}
329345
}
330346

347+
// ELEMENT: qualifier
348+
List<String> qualifier = contextService.qualifier;
349+
if (qualifier!= null) {
350+
for (String qualifierItem: qualifier) {
351+
if (qualifierItem!= null) {
352+
writer.writeStartElement(prefix, "qualifier", "http://java.sun.com/xml/ns/javaee");
353+
writer.writeCharacters(qualifierItem);
354+
writer.writeEndElement();
355+
}
356+
}
357+
}
358+
331359
// ELEMENT: property
332360
List<Property> property = contextService.property;
333361
if (property!= null) {

container/openejb-jee-accessors/src/main/java/org/apache/openejb/jee/ManagedExecutor$JAXB.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public static final ManagedExecutor _read(XoXMLStreamReader reader, RuntimeConte
8484
ManagedExecutor managedExecutor = new ManagedExecutor();
8585
context.beforeUnmarshal(managedExecutor, LifecycleCallback.NONE);
8686

87+
List<String> qualifier = null;
8788
List<Property> properties = null;
8889

8990
// Check xsi:type
@@ -123,6 +124,22 @@ public static final ManagedExecutor _read(XoXMLStreamReader reader, RuntimeConte
123124
// ELEMENT: maxAsync
124125
Integer maxAsync = Integer.valueOf(elementReader.getElementText());
125126
managedExecutor.maxAsync = maxAsync;
127+
} else if (("virtual" == elementReader.getLocalName())&&("http://java.sun.com/xml/ns/javaee" == elementReader.getNamespaceURI())) {
128+
// ELEMENT: virtual
129+
Boolean virtual = Boolean.valueOf(elementReader.getElementText());
130+
managedExecutor.virtual = virtual;
131+
} else if (("qualifier" == elementReader.getLocalName())&&("http://java.sun.com/xml/ns/javaee" == elementReader.getNamespaceURI())) {
132+
// ELEMENT: qualifier
133+
String qualifierItem = elementReader.getElementText();
134+
if (qualifier == null) {
135+
qualifier = managedExecutor.qualifier;
136+
if (qualifier!= null) {
137+
qualifier.clear();
138+
} else {
139+
qualifier = new ArrayList<>();
140+
}
141+
}
142+
qualifier.add(qualifierItem);
126143
} else if (("properties" == elementReader.getLocalName())&&("http://java.sun.com/xml/ns/javaee" == elementReader.getNamespaceURI())) {
127144
// ELEMENT: properties
128145
Property propertiesItem = readProperty(elementReader, context);
@@ -136,9 +153,12 @@ public static final ManagedExecutor _read(XoXMLStreamReader reader, RuntimeConte
136153
}
137154
properties.add(propertiesItem);
138155
} else {
139-
context.unexpectedElement(elementReader, new QName("http://java.sun.com/xml/ns/javaee", "description"), new QName("http://java.sun.com/xml/ns/javaee", "name"), new QName("http://java.sun.com/xml/ns/javaee", "context-service-ref"), new QName("http://java.sun.com/xml/ns/javaee", "hung-task-threshold"), new QName("http://java.sun.com/xml/ns/javaee", "max-async"), new QName("http://java.sun.com/xml/ns/javaee", "properties"));
156+
context.unexpectedElement(elementReader, new QName("http://java.sun.com/xml/ns/javaee", "description"), new QName("http://java.sun.com/xml/ns/javaee", "name"), new QName("http://java.sun.com/xml/ns/javaee", "context-service-ref"), new QName("http://java.sun.com/xml/ns/javaee", "hung-task-threshold"), new QName("http://java.sun.com/xml/ns/javaee", "max-async"), new QName("http://java.sun.com/xml/ns/javaee", "virtual"), new QName("http://java.sun.com/xml/ns/javaee", "qualifier"), new QName("http://java.sun.com/xml/ns/javaee", "properties"));
140157
}
141158
}
159+
if (qualifier!= null) {
160+
managedExecutor.qualifier = qualifier;
161+
}
142162
if (properties!= null) {
143163
managedExecutor.properties = properties;
144164
}
@@ -215,6 +235,26 @@ public static final void _write(XoXMLStreamWriter writer, ManagedExecutor manage
215235
writer.writeEndElement();
216236
}
217237

238+
// ELEMENT: virtual
239+
Boolean virtual = managedExecutor.virtual;
240+
if (virtual!= null) {
241+
writer.writeStartElement(prefix, "virtual", "http://java.sun.com/xml/ns/javaee");
242+
writer.writeCharacters(Boolean.toString(virtual));
243+
writer.writeEndElement();
244+
}
245+
246+
// ELEMENT: qualifier
247+
List<String> qualifier = managedExecutor.qualifier;
248+
if (qualifier!= null) {
249+
for (String qualifierItem: qualifier) {
250+
if (qualifierItem!= null) {
251+
writer.writeStartElement(prefix, "qualifier", "http://java.sun.com/xml/ns/javaee");
252+
writer.writeCharacters(qualifierItem);
253+
writer.writeEndElement();
254+
}
255+
}
256+
}
257+
218258
// ELEMENT: properties
219259
List<Property> properties = managedExecutor.properties;
220260
if (properties!= null) {

0 commit comments

Comments
 (0)