Skip to content

Commit

Permalink
TCK Tracking: Jakarta EE 10 Core Profile #6799 (#6885)
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <[email protected]>
  • Loading branch information
jbescos authored Aug 9, 2023
1 parent b3b533b commit fffdc7a
Show file tree
Hide file tree
Showing 54 changed files with 1,927 additions and 63 deletions.
2 changes: 1 addition & 1 deletion dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
<version.lib.opentracing.grpc>0.2.1</version.lib.opentracing.grpc>
<version.lib.opentracing.tracerresolver>0.1.8</version.lib.opentracing.tracerresolver>
<version.lib.perfmark-api>0.25.0</version.lib.perfmark-api>
<version.lib.parsson>1.0.2</version.lib.parsson>
<version.lib.parsson>1.1.2</version.lib.parsson>
<version.lib.postgresql>42.4.3</version.lib.postgresql>
<version.lib.prometheus>0.16.0</version.lib.prometheus>
<version.lib.reactivestreams>1.0.4</version.lib.reactivestreams>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,20 @@ private void processAnnotatedType(@Observes @WithAnnotations(ConfigProperties.cl
private <X> void harvestConfigPropertyInjectionPointsFromEnabledObserverMethod(@Observes ProcessObserverMethod<?, X> event,
BeanManager beanManager) {
AnnotatedMethod<X> annotatedMethod = event.getAnnotatedMethod();
List<AnnotatedParameter<X>> annotatedParameters = annotatedMethod.getParameters();
if (annotatedParameters != null) {
for (AnnotatedParameter<?> annotatedParameter : annotatedParameters) {
if ((annotatedParameter != null)
&& !annotatedParameter.isAnnotationPresent(Observes.class)) {
InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedParameter);
Set<Annotation> qualifiers = injectionPoint.getQualifiers();
assert qualifiers != null;
for (Annotation qualifier : qualifiers) {
if (qualifier instanceof ConfigProperty) {
ips.add(injectionPoint);
break;
if (annotatedMethod != null) {
List<AnnotatedParameter<X>> annotatedParameters = annotatedMethod.getParameters();
if (annotatedParameters != null) {
for (AnnotatedParameter<?> annotatedParameter : annotatedParameters) {
if ((annotatedParameter != null)
&& !annotatedParameter.isAnnotationPresent(Observes.class)) {
InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedParameter);
Set<Annotation> qualifiers = injectionPoint.getQualifiers();
assert qualifiers != null;
for (Annotation qualifier : qualifiers) {
if (qualifier instanceof ConfigProperty) {
ips.add(injectionPoint);
break;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022 Oracle and/or its affiliates.
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,6 +36,7 @@
* is empty)</li>
* <li>replaceConfigSourcesWithMp: (Optional) defaults to false: whether to replace config sources with microprofile if it
* exists</li>
* <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>
* </ul>
*/
public class HelidonContainerConfiguration implements ContainerConfiguration {
Expand All @@ -45,6 +46,7 @@ public class HelidonContainerConfiguration implements ContainerConfiguration {
private boolean deleteTmp = true;
private boolean useRelativePath = false;
private boolean useParentClassloader = true;
private boolean inWebContainer = false;
private final List<Consumer<ConfigBuilder>> builderConsumers = new ArrayList<>();

/**
Expand Down Expand Up @@ -104,6 +106,14 @@ public void setUseParentClassloader(boolean useParentClassloader) {
this.useParentClassloader = useParentClassloader;
}

public boolean isInWebContainer() {
return inWebContainer;
}

public void setInWebContainer(boolean inWebContainer) {
this.inWebContainer = inWebContainer;
}

@Override
public void validate() throws ConfigurationException {
if ((port <= 0) || (port > Short.MAX_VALUE)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022 Oracle and/or its affiliates.
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@
package io.helidon.microprofile.arquillian;

import java.lang.reflect.Method;
import java.util.Optional;

import jakarta.enterprise.context.control.RequestContextController;
import jakarta.enterprise.context.spi.CreationalContext;
Expand All @@ -29,6 +28,7 @@
import org.jboss.arquillian.core.spi.LoadableExtension;
import org.jboss.arquillian.test.spi.TestEnricher;
import org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher;
import org.testng.annotations.Test;

/**
* An arquillian LoadableExtension defining the {@link HelidonDeployableContainer}.
Expand All @@ -44,6 +44,8 @@ class HelidonContainerExtension implements LoadableExtension {
*/
static class HelidonCDIInjectionEnricher extends CDIInjectionEnricher {

private static final String ARQUILLIAN_DATA_PROVIDER = "ARQUILLIAN_DATA_PROVIDER";
private static final Object[] EMPTY = new Object[0];
private BeanManager beanManager;
private RequestContextController requestContextController;

Expand Down Expand Up @@ -78,11 +80,11 @@ public RequestContextController getRequestContextController() {

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

private static CDI<Object> cdi() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -45,12 +47,17 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import io.helidon.config.mp.MpConfigSources;

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.enterprise.inject.spi.DefinitionException;
import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.ws.rs.core.Application;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.spi.ConfigBuilder;
Expand All @@ -64,9 +71,13 @@
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

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

Path rootDir = context.deployDir.resolve("");
if (isJavaArchive) {
ensureBeansXml(rootDir);
ensureBeansXml(rootDir, null);
classPath.add(rootDir);
} else {
// Prepare the launcher files
Path webInfDir = context.deployDir.resolve("WEB-INF");
Path classesDir = webInfDir.resolve("classes");
Path libDir = webInfDir.resolve("lib");
ensureBeansXml(classesDir);
ensureBeansXml(classesDir, webInfDir);
addServerClasspath(classPath, classesDir, libDir, rootDir);
if (containerConfig.isInWebContainer()) {
context.rootContext = archive.getName().split("\\.")[0];
if (!loadApplicationFromWebXml(context, webInfDir)) {
// Search Application in classes
loadApplicationFromClasses(context, archive);
}
}
}

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

private boolean loadApplicationFromClasses(RunContext context, Archive<?> archive)
throws ClassNotFoundException, ReflectiveOperationException {
ArchivePath classes = ArchivePaths.create("WEB-INF", "classes");
org.jboss.shrinkwrap.api.Node root = archive.getContent().get(classes);
Collection<Class<Application>> applications = new HashSet<>();
if (root != null) {
deepApplicationFind(root, applications);
context.applications.addAll(applications);
}
return !context.applications.isEmpty();
}

private void deepApplicationFind(org.jboss.shrinkwrap.api.Node parent,
Collection<Class<Application>> applications) throws ClassNotFoundException {
for (org.jboss.shrinkwrap.api.Node child : parent.getChildren()) {
if (child.getChildren().isEmpty()) {
String name = child.toString();
if (name.endsWith(".class")) {
name = name.replaceFirst("\\.class", "").replaceFirst("/WEB-INF/classes/", "").replaceAll("/", ".");
Class<?> clazz = Class.forName(name);
if (Application.class.isAssignableFrom(clazz)) {
applications.add((Class<Application>) clazz);
}
}
} else {
deepApplicationFind(child, applications);
}
}
}

private boolean loadApplicationFromWebXml(RunContext context, Path webInfDir) throws IOException,
ParserConfigurationException, SAXException, ReflectiveOperationException {
Path webXml = webInfDir.resolve("web.xml");
if (Files.exists(webXml)) {
try (InputStream inputStream = Files.newInputStream(webXml)) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(inputStream);
NodeList nodes = doc.getElementsByTagName("init-param");
for (int i = 0; i < nodes.getLength(); i++) {
NodeList childs = nodes.item(i).getChildNodes();
Class<Application> application = application(childs);
if (application != null) {
context.applications.add(application);
return true;
}
}
}
}
return false;
}

private Class<Application> application(NodeList childs) throws ClassNotFoundException {
boolean isApp = false;
String appName = null;
for (int j = 0; j < childs.getLength(); j++) {
org.w3c.dom.Node element = childs.item(j);
String name = element.getNodeName();
String value = element.getTextContent();
if ("param-name".equals(name) && "jakarta.ws.rs.Application".equals(value)) {
isApp = true;
} else if ("param-value".equals(name)) {
appName = value;
}
}
if (isApp) {
return (Class<Application>) Class.forName(appName);
} else {
return null;
}
}

static Optional<Exception> lookForSupressedDeploymentException(Throwable t) {
if (t == null) {
return Optional.empty();
Expand Down Expand Up @@ -290,6 +381,18 @@ void startServer(RunContext context, Path[] classPath)
// META-INF/microprofile-config.properties (such as JWT-Auth)
ConfigBuilder builder = ConfigProviderResolver.instance()
.getBuilder();
// Add root context path per Application
if (context.rootContext != null && !context.applications.isEmpty()) {
containerConfig.addConfigBuilderConsumer(configBuilder -> {
Map<String, String> properties = new HashMap<>();
for (Class<Application> app : context.applications) {
String key = app.getName() + ".routing-path.path";
String value = "/" + context.rootContext;
properties.put(key, value);
}
configBuilder.withSources(MpConfigSources.create(properties));
});
}
// we must use the default configuration to support profiles (and test them correctly in config TCK)
// we may need to have a custom configuration for TCKs that do require workarounds
/*
Expand Down Expand Up @@ -376,17 +479,24 @@ void addServerClasspath(List<Path> classpath, Path classesDir, Path libDir, Path
classpath.add(rootDir);
}

private void ensureBeansXml(Path classesDir) throws IOException {
private void ensureBeansXml(Path classesDir, Path webinfDir) throws IOException {
Path beansPath = classesDir.resolve("META-INF/beans.xml");
Path metaInfPath = beansPath.getParent();
if (null != metaInfPath) {
Files.createDirectories(metaInfPath);
}
if (containerConfig.isInWebContainer() && webinfDir != null) {
// In case exists WEB-INF/beans.xml, then move it to classes/META-INF/beans.xml
Path webInfBeansPath = webinfDir.resolve("beans.xml");
if (Files.exists(webInfBeansPath)) {
Files.move(webInfBeansPath, beansPath);
return;
}
}
if (Files.exists(beansPath)) {
return;
}
try (InputStream beanXmlTemplate = HelidonDeployableContainer.class.getResourceAsStream("/templates/beans.xml")) {
Path metaInfPath = beansPath.getParent();
if (null != metaInfPath) {
Files.createDirectories(metaInfPath);
}

if (null == beanXmlTemplate) {
Files.write(beansPath, new byte[0]);
} else {
Expand Down Expand Up @@ -531,6 +641,8 @@ private static class RunContext {
private Object runner;
// existing class loader
private ClassLoader oldClassLoader;
private String rootContext;
private Set<Class<Application>> applications = new HashSet<>();
}

static class HelidonContainerClassloader extends ClassLoader implements Closeable {
Expand Down Expand Up @@ -576,8 +688,10 @@ public Enumeration<URL> getResources(String name) throws IOException {
}
}
}

return Collections.enumeration(result);
// Give priority to WebApp resources (for example ServiceLoader provided by WebApp)
List<URL> toRevert = new ArrayList<URL>(result);
Collections.reverse(toRevert);
return Collections.enumeration(toRevert);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2018, 2022 Oracle and/or its affiliates.
Copyright (c) 2018, 2023 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -19,8 +19,8 @@
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd"
version="3.0"
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
version="4.0"
bean-discovery-mode="all">
</beans>

Loading

0 comments on commit fffdc7a

Please sign in to comment.