Skip to content

Commit fffdc7a

Browse files
authored
TCK Tracking: Jakarta EE 10 Core Profile helidon-io#6799 (helidon-io#6885)
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
1 parent b3b533b commit fffdc7a

54 files changed

Lines changed: 1927 additions & 63 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dependencies/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
<version.lib.opentracing.grpc>0.2.1</version.lib.opentracing.grpc>
141141
<version.lib.opentracing.tracerresolver>0.1.8</version.lib.opentracing.tracerresolver>
142142
<version.lib.perfmark-api>0.25.0</version.lib.perfmark-api>
143-
<version.lib.parsson>1.0.2</version.lib.parsson>
143+
<version.lib.parsson>1.1.2</version.lib.parsson>
144144
<version.lib.postgresql>42.4.3</version.lib.postgresql>
145145
<version.lib.prometheus>0.16.0</version.lib.prometheus>
146146
<version.lib.reactivestreams>1.0.4</version.lib.reactivestreams>

microprofile/config/src/main/java/io/helidon/microprofile/config/ConfigCdiExtension.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,20 @@ private void processAnnotatedType(@Observes @WithAnnotations(ConfigProperties.cl
136136
private <X> void harvestConfigPropertyInjectionPointsFromEnabledObserverMethod(@Observes ProcessObserverMethod<?, X> event,
137137
BeanManager beanManager) {
138138
AnnotatedMethod<X> annotatedMethod = event.getAnnotatedMethod();
139-
List<AnnotatedParameter<X>> annotatedParameters = annotatedMethod.getParameters();
140-
if (annotatedParameters != null) {
141-
for (AnnotatedParameter<?> annotatedParameter : annotatedParameters) {
142-
if ((annotatedParameter != null)
143-
&& !annotatedParameter.isAnnotationPresent(Observes.class)) {
144-
InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedParameter);
145-
Set<Annotation> qualifiers = injectionPoint.getQualifiers();
146-
assert qualifiers != null;
147-
for (Annotation qualifier : qualifiers) {
148-
if (qualifier instanceof ConfigProperty) {
149-
ips.add(injectionPoint);
150-
break;
139+
if (annotatedMethod != null) {
140+
List<AnnotatedParameter<X>> annotatedParameters = annotatedMethod.getParameters();
141+
if (annotatedParameters != null) {
142+
for (AnnotatedParameter<?> annotatedParameter : annotatedParameters) {
143+
if ((annotatedParameter != null)
144+
&& !annotatedParameter.isAnnotationPresent(Observes.class)) {
145+
InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedParameter);
146+
Set<Annotation> qualifiers = injectionPoint.getQualifiers();
147+
assert qualifiers != null;
148+
for (Annotation qualifier : qualifiers) {
149+
if (qualifier instanceof ConfigProperty) {
150+
ips.add(injectionPoint);
151+
break;
152+
}
151153
}
152154
}
153155
}

microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerConfiguration.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2022 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
3636
* is empty)</li>
3737
* <li>replaceConfigSourcesWithMp: (Optional) defaults to false: whether to replace config sources with microprofile if it
3838
* exists</li>
39+
* <li>inWebContainer: defaults to false: sets web app context root, load WEB-INF/beans.xml and find any jakarta.ws.rs.core.Application in the webapp classes</li>
3940
* </ul>
4041
*/
4142
public class HelidonContainerConfiguration implements ContainerConfiguration {
@@ -45,6 +46,7 @@ public class HelidonContainerConfiguration implements ContainerConfiguration {
4546
private boolean deleteTmp = true;
4647
private boolean useRelativePath = false;
4748
private boolean useParentClassloader = true;
49+
private boolean inWebContainer = false;
4850
private final List<Consumer<ConfigBuilder>> builderConsumers = new ArrayList<>();
4951

5052
/**
@@ -104,6 +106,14 @@ public void setUseParentClassloader(boolean useParentClassloader) {
104106
this.useParentClassloader = useParentClassloader;
105107
}
106108

109+
public boolean isInWebContainer() {
110+
return inWebContainer;
111+
}
112+
113+
public void setInWebContainer(boolean inWebContainer) {
114+
this.inWebContainer = inWebContainer;
115+
}
116+
107117
@Override
108118
public void validate() throws ConfigurationException {
109119
if ((port <= 0) || (port > Short.MAX_VALUE)) {

microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerExtension.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2022 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package io.helidon.microprofile.arquillian;
1818

1919
import java.lang.reflect.Method;
20-
import java.util.Optional;
2120

2221
import jakarta.enterprise.context.control.RequestContextController;
2322
import jakarta.enterprise.context.spi.CreationalContext;
@@ -29,6 +28,7 @@
2928
import org.jboss.arquillian.core.spi.LoadableExtension;
3029
import org.jboss.arquillian.test.spi.TestEnricher;
3130
import org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher;
31+
import org.testng.annotations.Test;
3232

3333
/**
3434
* An arquillian LoadableExtension defining the {@link HelidonDeployableContainer}.
@@ -44,6 +44,8 @@ class HelidonContainerExtension implements LoadableExtension {
4444
*/
4545
static class HelidonCDIInjectionEnricher extends CDIInjectionEnricher {
4646

47+
private static final String ARQUILLIAN_DATA_PROVIDER = "ARQUILLIAN_DATA_PROVIDER";
48+
private static final Object[] EMPTY = new Object[0];
4749
private BeanManager beanManager;
4850
private RequestContextController requestContextController;
4951

@@ -78,11 +80,11 @@ public RequestContextController getRequestContextController() {
7880

7981
@Override
8082
public Object[] resolve(Method method) {
81-
return Optional.ofNullable(method.getAnnotation(org.testng.annotations.Test.class))
82-
.filter(test -> !test.dataProvider().isEmpty())
83-
// Don't resolve TestNG data providers parameters as cdi beans
84-
.map(unused -> new Object[0])
85-
.orElseGet(() -> super.resolve(method));
83+
Test test = method.getAnnotation(org.testng.annotations.Test.class);
84+
if (test != null && !ARQUILLIAN_DATA_PROVIDER.equals(test.dataProvider())) {
85+
return EMPTY;
86+
}
87+
return super.resolve(method);
8688
}
8789

8890
private static CDI<Object> cdi() {

microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java

Lines changed: 125 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
import java.nio.file.Paths;
3333
import java.nio.file.StandardCopyOption;
3434
import java.util.ArrayList;
35+
import java.util.Collection;
3536
import java.util.Collections;
3637
import java.util.Comparator;
3738
import java.util.Enumeration;
3839
import java.util.HashMap;
40+
import java.util.HashSet;
3941
import java.util.LinkedHashSet;
4042
import java.util.List;
4143
import java.util.Map;
@@ -45,12 +47,17 @@
4547
import java.util.concurrent.ConcurrentLinkedQueue;
4648
import java.util.regex.Pattern;
4749

50+
import javax.xml.parsers.DocumentBuilder;
51+
import javax.xml.parsers.DocumentBuilderFactory;
52+
import javax.xml.parsers.ParserConfigurationException;
53+
4854
import io.helidon.config.mp.MpConfigSources;
4955

5056
import jakarta.enterprise.inject.se.SeContainer;
5157
import jakarta.enterprise.inject.spi.CDI;
5258
import jakarta.enterprise.inject.spi.DefinitionException;
5359
import jakarta.enterprise.util.AnnotationLiteral;
60+
import jakarta.ws.rs.core.Application;
5461
import org.eclipse.microprofile.config.Config;
5562
import org.eclipse.microprofile.config.ConfigProvider;
5663
import org.eclipse.microprofile.config.spi.ConfigBuilder;
@@ -64,9 +71,13 @@
6471
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
6572
import org.jboss.shrinkwrap.api.Archive;
6673
import org.jboss.shrinkwrap.api.ArchivePath;
74+
import org.jboss.shrinkwrap.api.ArchivePaths;
6775
import org.jboss.shrinkwrap.api.Node;
6876
import org.jboss.shrinkwrap.api.spec.JavaArchive;
6977
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
78+
import org.w3c.dom.Document;
79+
import org.w3c.dom.NodeList;
80+
import org.xml.sax.SAXException;
7081

7182
/**
7283
* Implementation of DeployableContainer for launching Helidon microprofile server.
@@ -77,6 +88,7 @@
7788
* <li>A temporary directory is created</li>
7889
* <li>The WebArchive contents are written to the temporary directory</li>
7990
* <li>beans.xml is created in WEB-INF/classes if not present</li>
91+
* <li>WEB-INF/beans.xml will be moved to WEB-INF/classes/META-INF if present</li>
8092
* <li>The server is started with WEB-INF/classes and all libraries in WEB-INF/libon the classpath.</li>
8193
* </ol>
8294
*
@@ -180,19 +192,26 @@ public ProtocolMetaData deploy(Archive<?> archive) throws DeploymentException {
180192

181193
Path rootDir = context.deployDir.resolve("");
182194
if (isJavaArchive) {
183-
ensureBeansXml(rootDir);
195+
ensureBeansXml(rootDir, null);
184196
classPath.add(rootDir);
185197
} else {
186198
// Prepare the launcher files
187199
Path webInfDir = context.deployDir.resolve("WEB-INF");
188200
Path classesDir = webInfDir.resolve("classes");
189201
Path libDir = webInfDir.resolve("lib");
190-
ensureBeansXml(classesDir);
202+
ensureBeansXml(classesDir, webInfDir);
191203
addServerClasspath(classPath, classesDir, libDir, rootDir);
204+
if (containerConfig.isInWebContainer()) {
205+
context.rootContext = archive.getName().split("\\.")[0];
206+
if (!loadApplicationFromWebXml(context, webInfDir)) {
207+
// Search Application in classes
208+
loadApplicationFromClasses(context, archive);
209+
}
210+
}
192211
}
193212

194213
startServer(context, classPath.toArray(new Path[0]));
195-
} catch (IOException e) {
214+
} catch (IOException | SAXException | ParserConfigurationException e) {
196215
LOGGER.log(Level.INFO, "Failed to start container", e);
197216
throw new DeploymentException("Failed to copy the archive assets into the deployment directory", e);
198217
} catch (InvocationTargetException e) {
@@ -221,6 +240,78 @@ public ProtocolMetaData deploy(Archive<?> archive) throws DeploymentException {
221240
return new ProtocolMetaData();
222241
}
223242

243+
private boolean loadApplicationFromClasses(RunContext context, Archive<?> archive)
244+
throws ClassNotFoundException, ReflectiveOperationException {
245+
ArchivePath classes = ArchivePaths.create("WEB-INF", "classes");
246+
org.jboss.shrinkwrap.api.Node root = archive.getContent().get(classes);
247+
Collection<Class<Application>> applications = new HashSet<>();
248+
if (root != null) {
249+
deepApplicationFind(root, applications);
250+
context.applications.addAll(applications);
251+
}
252+
return !context.applications.isEmpty();
253+
}
254+
255+
private void deepApplicationFind(org.jboss.shrinkwrap.api.Node parent,
256+
Collection<Class<Application>> applications) throws ClassNotFoundException {
257+
for (org.jboss.shrinkwrap.api.Node child : parent.getChildren()) {
258+
if (child.getChildren().isEmpty()) {
259+
String name = child.toString();
260+
if (name.endsWith(".class")) {
261+
name = name.replaceFirst("\\.class", "").replaceFirst("/WEB-INF/classes/", "").replaceAll("/", ".");
262+
Class<?> clazz = Class.forName(name);
263+
if (Application.class.isAssignableFrom(clazz)) {
264+
applications.add((Class<Application>) clazz);
265+
}
266+
}
267+
} else {
268+
deepApplicationFind(child, applications);
269+
}
270+
}
271+
}
272+
273+
private boolean loadApplicationFromWebXml(RunContext context, Path webInfDir) throws IOException,
274+
ParserConfigurationException, SAXException, ReflectiveOperationException {
275+
Path webXml = webInfDir.resolve("web.xml");
276+
if (Files.exists(webXml)) {
277+
try (InputStream inputStream = Files.newInputStream(webXml)) {
278+
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
279+
DocumentBuilder db = dbf.newDocumentBuilder();
280+
Document doc = db.parse(inputStream);
281+
NodeList nodes = doc.getElementsByTagName("init-param");
282+
for (int i = 0; i < nodes.getLength(); i++) {
283+
NodeList childs = nodes.item(i).getChildNodes();
284+
Class<Application> application = application(childs);
285+
if (application != null) {
286+
context.applications.add(application);
287+
return true;
288+
}
289+
}
290+
}
291+
}
292+
return false;
293+
}
294+
295+
private Class<Application> application(NodeList childs) throws ClassNotFoundException {
296+
boolean isApp = false;
297+
String appName = null;
298+
for (int j = 0; j < childs.getLength(); j++) {
299+
org.w3c.dom.Node element = childs.item(j);
300+
String name = element.getNodeName();
301+
String value = element.getTextContent();
302+
if ("param-name".equals(name) && "jakarta.ws.rs.Application".equals(value)) {
303+
isApp = true;
304+
} else if ("param-value".equals(name)) {
305+
appName = value;
306+
}
307+
}
308+
if (isApp) {
309+
return (Class<Application>) Class.forName(appName);
310+
} else {
311+
return null;
312+
}
313+
}
314+
224315
static Optional<Exception> lookForSupressedDeploymentException(Throwable t) {
225316
if (t == null) {
226317
return Optional.empty();
@@ -290,6 +381,18 @@ void startServer(RunContext context, Path[] classPath)
290381
// META-INF/microprofile-config.properties (such as JWT-Auth)
291382
ConfigBuilder builder = ConfigProviderResolver.instance()
292383
.getBuilder();
384+
// Add root context path per Application
385+
if (context.rootContext != null && !context.applications.isEmpty()) {
386+
containerConfig.addConfigBuilderConsumer(configBuilder -> {
387+
Map<String, String> properties = new HashMap<>();
388+
for (Class<Application> app : context.applications) {
389+
String key = app.getName() + ".routing-path.path";
390+
String value = "/" + context.rootContext;
391+
properties.put(key, value);
392+
}
393+
configBuilder.withSources(MpConfigSources.create(properties));
394+
});
395+
}
293396
// we must use the default configuration to support profiles (and test them correctly in config TCK)
294397
// we may need to have a custom configuration for TCKs that do require workarounds
295398
/*
@@ -376,17 +479,24 @@ void addServerClasspath(List<Path> classpath, Path classesDir, Path libDir, Path
376479
classpath.add(rootDir);
377480
}
378481

379-
private void ensureBeansXml(Path classesDir) throws IOException {
482+
private void ensureBeansXml(Path classesDir, Path webinfDir) throws IOException {
380483
Path beansPath = classesDir.resolve("META-INF/beans.xml");
484+
Path metaInfPath = beansPath.getParent();
485+
if (null != metaInfPath) {
486+
Files.createDirectories(metaInfPath);
487+
}
488+
if (containerConfig.isInWebContainer() && webinfDir != null) {
489+
// In case exists WEB-INF/beans.xml, then move it to classes/META-INF/beans.xml
490+
Path webInfBeansPath = webinfDir.resolve("beans.xml");
491+
if (Files.exists(webInfBeansPath)) {
492+
Files.move(webInfBeansPath, beansPath);
493+
return;
494+
}
495+
}
381496
if (Files.exists(beansPath)) {
382497
return;
383498
}
384499
try (InputStream beanXmlTemplate = HelidonDeployableContainer.class.getResourceAsStream("/templates/beans.xml")) {
385-
Path metaInfPath = beansPath.getParent();
386-
if (null != metaInfPath) {
387-
Files.createDirectories(metaInfPath);
388-
}
389-
390500
if (null == beanXmlTemplate) {
391501
Files.write(beansPath, new byte[0]);
392502
} else {
@@ -531,6 +641,8 @@ private static class RunContext {
531641
private Object runner;
532642
// existing class loader
533643
private ClassLoader oldClassLoader;
644+
private String rootContext;
645+
private Set<Class<Application>> applications = new HashSet<>();
534646
}
535647

536648
static class HelidonContainerClassloader extends ClassLoader implements Closeable {
@@ -576,8 +688,10 @@ public Enumeration<URL> getResources(String name) throws IOException {
576688
}
577689
}
578690
}
579-
580-
return Collections.enumeration(result);
691+
// Give priority to WebApp resources (for example ServiceLoader provided by WebApp)
692+
List<URL> toRevert = new ArrayList<URL>(result);
693+
Collections.reverse(toRevert);
694+
return Collections.enumeration(toRevert);
581695
}
582696

583697
@Override

microprofile/tests/arquillian/src/main/resources/templates/beans.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright (c) 2018, 2022 Oracle and/or its affiliates.
4+
Copyright (c) 2018, 2023 Oracle and/or its affiliates.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -19,8 +19,8 @@
1919
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
2020
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2121
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
22-
https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd"
23-
version="3.0"
22+
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
23+
version="4.0"
2424
bean-discovery-mode="all">
2525
</beans>
2626

0 commit comments

Comments
 (0)