diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 419943c20baaa..43810566bee45 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -93,8 +93,8 @@ updates: # Agroal - dependency-name: io.agroal:* # Jandex - - dependency-name: org.jboss:jandex - - dependency-name: org.jboss.jandex:jandex-maven-plugin + - dependency-name: io.smallrye:jandex + - dependency-name: io.smallrye:jandex-maven-plugin # WireMock - dependency-name: com.github.tomakehurst:wiremock-jre8-standalone - dependency-name: uk.co.automatictester:wiremock-maven-plugin diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml index 01f4d938f4420..f8f1b5253b0f2 100644 --- a/.github/workflows/ci-actions-incremental.yml +++ b/.github/workflows/ci-actions-incremental.yml @@ -331,7 +331,7 @@ jobs: java-version: ${{ matrix.java.java-version }} - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -413,7 +413,7 @@ jobs: run: git config --global core.longpaths true - uses: actions/checkout@v3 - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -488,7 +488,7 @@ jobs: run: git config --global core.longpaths true - uses: actions/checkout@v3 - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -554,7 +554,7 @@ jobs: run: git config --global core.longpaths true - uses: actions/checkout@v3 - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -610,7 +610,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -661,7 +661,7 @@ jobs: distribution: temurin java-version: 11 - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . @@ -746,7 +746,7 @@ jobs: run: | cat <<< $(jq '.HttpHeaders += {"User-Agent": "Quarkus-CI-Docker-Client"}' ~/.docker/config.json) > ~/.docker/config.json - name: Download Maven Repo - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: name: maven-repo path: . diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 2b653ce0fc4d0..ec2645736247a 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -18,7 +18,7 @@ 1.0.2.3 1.0.12.3 3.0.2 - 2.4.3.Final + 3.0.0 4.7.7.Final 0.33.0 0.2.4 @@ -43,8 +43,8 @@ 2.12.0 3.3.0 3.0.5 - 2.2.1 - 1.7.1 + 2.3.0 + 1.8.0 2.1.1 5.5.0 3.5.3 @@ -53,7 +53,7 @@ 2.7.0 2.26.0 3.18.0 - 1.1.2 + 1.2.0 1.2.1 1.3.5 3.0.4 @@ -82,7 +82,7 @@ 2.1.0 22.2.0 ${graal-sdk.version} - 1.1.0.Final + 1.2.0.Final 2.13.4 1.0.0.Final 3.12.0 @@ -138,7 +138,7 @@ 1.0.3 3.5.0.Final 1.7.0 - 3.2.1 + 3.2.2 1.8.0 1.1.8.4 0.100.0 @@ -163,7 +163,7 @@ 6.0.0 4.7.1 1.5.2 - 0.33.10 + 0.34.0 3.24.2 3.14.9 1.17.2 @@ -177,7 +177,7 @@ 5.8.0 4.9.2 1.1.4.Final - 19.0.1 + 19.0.2 1.15.0 3.25.0 2.15.0 @@ -201,7 +201,7 @@ 2.6 0.10.0 - 9.24.4 + 9.25 0.0.6 0.1.1 @@ -4247,7 +4247,7 @@ ${graal-sdk.version} - org.jboss + io.smallrye jandex ${jandex.version} @@ -4855,6 +4855,12 @@ org.hibernate hibernate-core ${hibernate-orm.version} + + + org.jboss + jandex + + org.hibernate @@ -4901,6 +4907,10 @@ hibernate-search-mapper-orm ${hibernate-search.version} + + org.jboss + jandex + org.hibernate.common diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 2a9d7f36089c1..545ec3932ef57 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -33,9 +33,9 @@ 3.0.0 ${version.surefire.plugin} - - 1.2.3 - 0.13.0 + + 3.0.0 + 1.0.0 - 19.0.1 + 19.0.2 quay.io/keycloak/keycloak:${keycloak.version} quay.io/keycloak/keycloak:${keycloak.version}-legacy @@ -111,7 +111,7 @@ 3.23.1 - 2.33.2 + 2.34.0 7.2.0 @@ -599,9 +599,9 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin - ${jandex-maven-plugin.version} + ${jandex.version} net.revelc.code.formatter @@ -1034,7 +1034,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin @@ -1274,6 +1274,7 @@ io.quarkus.build-parent + io.quarkus.jakarta-json-cleanup diff --git a/core/deployment/pom.xml b/core/deployment/pom.xml index 18a27d767cca7..cd20dfdcceaa1 100644 --- a/core/deployment/pom.xml +++ b/core/deployment/pom.xml @@ -35,7 +35,7 @@ gizmo - org.jboss + io.smallrye jandex diff --git a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java index 861e77cb98b0f..61121cc405474 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java @@ -1,11 +1,8 @@ package io.quarkus.deployment; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.net.Socket; @@ -15,7 +12,6 @@ import java.util.List; import java.util.Optional; import java.util.function.BooleanSupplier; -import java.util.function.Function; import java.util.function.Supplier; import org.eclipse.microprofile.config.ConfigProvider; @@ -176,29 +172,6 @@ public Result get() { } } - public static class OutputFilter implements Function { - private final StringBuilder builder = new StringBuilder(); - - @Override - public Runnable apply(InputStream is) { - return () -> { - - try (InputStreamReader isr = new InputStreamReader(is); - BufferedReader reader = new BufferedReader(isr)) { - - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - builder.append(line); - } - } catch (IOException e) { - throw new RuntimeException("Error reading stream.", e); - } - }; - } - - public String getOutput() { - return builder.toString(); - } - } } private enum Result { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/OutputFilter.java b/core/deployment/src/main/java/io/quarkus/deployment/OutputFilter.java new file mode 100644 index 0000000000000..1d3af6e190939 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/OutputFilter.java @@ -0,0 +1,31 @@ +package io.quarkus.deployment; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.function.Function; + +public class OutputFilter implements Function { + private final StringBuilder builder = new StringBuilder(); + + @Override + public Runnable apply(InputStream is) { + return () -> { + + try (InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + builder.append(line); + } + } catch (IOException e) { + throw new RuntimeException("Error reading stream.", e); + } + }; + } + + public String getOutput() { + return builder.toString(); + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index e1c0873b86691..57df8f03e13e7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -484,7 +484,7 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) { int index = 0; for (Path i : changedClassResults.changedClasses) { byte[] bytes = Files.readAllBytes(i); - String name = indexer.index(new ByteArrayInputStream(bytes)).name().toString(); + String name = indexer.indexWithSummary(new ByteArrayInputStream(bytes)).name().toString(); defs[index++] = new ClassDefinition( Thread.currentThread().getContextClassLoader().loadClass(name), classTransformers.apply(name, bytes)); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexWrapper.java b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexWrapper.java index 6da05bc210274..904e38123913c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexWrapper.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexWrapper.java @@ -3,13 +3,16 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Queue; import java.util.Set; import java.util.stream.Collectors; @@ -91,6 +94,73 @@ public Collection getAllKnownSubclasses(DotName className) { return allKnown; } + private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { + final Set subClassesToProcess = new HashSet(); + subClassesToProcess.add(className); + while (!subClassesToProcess.isEmpty()) { + final Iterator toProcess = subClassesToProcess.iterator(); + DotName name = toProcess.next(); + toProcess.remove(); + processedClasses.add(name); + getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + } + } + + private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, + Set processedClasses) { + final Collection directSubclasses = getKnownDirectSubclasses(name); + if (directSubclasses != null) { + for (final ClassInfo clazz : directSubclasses) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + allKnown.add(clazz); + subClassesToProcess.add(className); + } + } + } + } + + @Override + public Collection getKnownDirectSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getKnownDirectSubinterfaces(interfaceName); + } + Set directSubinterfaces = new HashSet<>(index.getKnownDirectSubinterfaces(interfaceName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isPresent() && additional.get().interfaceNames().contains(interfaceName)) { + directSubinterfaces.add(additional.get()); + } + } + return directSubinterfaces; + } + + @Override + public Collection getAllKnownSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getAllKnownSubinterfaces(interfaceName); + } + + Set result = new HashSet<>(); + + Queue workQueue = new ArrayDeque<>(); + Set alreadyProcessed = new HashSet<>(); + + workQueue.add(interfaceName); + while (!workQueue.isEmpty()) { + DotName iface = workQueue.remove(); + if (!alreadyProcessed.add(iface)) { + continue; + } + + for (ClassInfo directSubinterface : getKnownDirectSubinterfaces(iface)) { + result.add(directSubinterface); + workQueue.add(directSubinterface.name()); + } + } + + return result; + } + @Override public Collection getKnownDirectImplementors(DotName className) { if (additionalClasses.isEmpty()) { @@ -130,6 +200,27 @@ public Collection getAllKnownImplementors(DotName interfaceName) { return allKnown; } + private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, + Set processedClasses) { + final Collection list = getKnownDirectImplementors(name); + if (list != null) { + for (final ClassInfo clazz : list) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + if (Modifier.isInterface(clazz.flags())) { + subInterfacesToProcess.add(className); + } else { + if (!allKnown.contains(clazz)) { + allKnown.add(clazz); + processedClasses.add(className); + getAllKnownSubClasses(className, allKnown, processedClasses); + } + } + } + } + } + } + @Override public Collection getAnnotations(DotName annotationName) { return index.getAnnotations(annotationName); @@ -155,51 +246,44 @@ public Collection getKnownUsers(DotName className) { return this.index.getKnownUsers(className); } - private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { - final Set subClassesToProcess = new HashSet(); - subClassesToProcess.add(className); - while (!subClassesToProcess.isEmpty()) { - final Iterator toProcess = subClassesToProcess.iterator(); - DotName name = toProcess.next(); - toProcess.remove(); - processedClasses.add(name); - getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + @Override + public Collection getClassesInPackage(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getClassesInPackage(packageName); } - } - - private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, - Set processedClasses) { - final Collection directSubclasses = getKnownDirectSubclasses(name); - if (directSubclasses != null) { - for (final ClassInfo clazz : directSubclasses) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - allKnown.add(clazz); - subClassesToProcess.add(className); - } + Set classesInPackage = new HashSet<>(index.getClassesInPackage(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + if (Objects.equals(packageName, additional.get().name().packagePrefixName())) { + classesInPackage.add(additional.get()); } } + return classesInPackage; } - private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, - Set processedClasses) { - final Collection list = getKnownDirectImplementors(name); - if (list != null) { - for (final ClassInfo clazz : list) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - if (Modifier.isInterface(clazz.flags())) { - subInterfacesToProcess.add(className); - } else { - if (!allKnown.contains(clazz)) { - allKnown.add(clazz); - processedClasses.add(className); - getAllKnownSubClasses(className, allKnown, processedClasses); - } - } + @Override + public Set getSubpackages(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getSubpackages(packageName); + } + Set subpackages = new HashSet<>(index.getSubpackages(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + DotName pkg = additional.get().name().packagePrefixName(); + while (pkg != null) { + DotName superPkg = pkg.packagePrefixName(); + if (superPkg != null && superPkg.equals(packageName)) { + subpackages.add(pkg); } + pkg = superPkg; } + } + return subpackages; } private Optional computeAdditional(DotName className) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java index 2030fca7c05cb..7a573e3b77f62 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java @@ -19,6 +19,7 @@ import java.util.zip.ZipEntry; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassSummary; import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.IndexReader; @@ -134,20 +135,29 @@ public static void indexClass(String className, Indexer indexer, IndexView quark if (additionalIndex.contains(classDotName)) { return; } + + DotName superclassName; + Set annotationNames; + ClassInfo classInfo = quarkusIndex.getClassByName(classDotName); if (classInfo == null) { log.debugf("Index class: %s", className); try (InputStream stream = IoUtil.readClass(classLoader, className)) { - classInfo = indexer.index(stream); - additionalIndex.add(classInfo.name()); + ClassSummary summary = indexer.indexWithSummary(stream); + additionalIndex.add(summary.name()); + superclassName = summary.superclassName(); + annotationNames = summary.annotations(); } catch (Exception e) { throw new IllegalStateException("Failed to index: " + className, e); } } else { // The class could be indexed by quarkus - we still need to distinguish framework classes additionalIndex.add(classDotName); + superclassName = classInfo.superName(); + annotationNames = classInfo.annotationsMap().keySet(); } - for (DotName annotationName : classInfo.annotationsMap().keySet()) { + + for (DotName annotationName : annotationNames) { if (!additionalIndex.contains(annotationName) && quarkusIndex.getClassByName(annotationName) == null) { try (InputStream annotationStream = IoUtil.readClass(classLoader, annotationName.toString())) { if (annotationStream == null) { @@ -162,8 +172,8 @@ public static void indexClass(String className, Indexer indexer, IndexView quark } } } - if (classInfo.superName() != null && !classInfo.superName().equals(OBJECT)) { - indexClass(classInfo.superName().toString(), indexer, quarkusIndex, additionalIndex, classLoader); + if (superclassName != null && !superclassName.equals(OBJECT)) { + indexClass(superclassName.toString(), indexer, quarkusIndex, additionalIndex, classLoader); } } @@ -174,20 +184,26 @@ public static void indexClass(String className, Indexer indexer, if (additionalIndex.contains(classDotName)) { return; } + + Set annotationNames; + ClassInfo classInfo = quarkusIndex.getClassByName(classDotName); if (classInfo == null) { log.debugf("Index class: %s", className); try (InputStream stream = new ByteArrayInputStream(beanData)) { - classInfo = indexer.index(stream); - additionalIndex.add(classInfo.name()); + ClassSummary summary = indexer.indexWithSummary(stream); + additionalIndex.add(summary.name()); + annotationNames = summary.annotations(); } catch (IOException e) { throw new IllegalStateException("Failed to index: " + className, e); } } else { // The class could be indexed by quarkus - we still need to distinguish framework classes additionalIndex.add(classDotName); + annotationNames = classInfo.annotationsMap().keySet(); } - for (DotName annotationName : classInfo.annotationsMap().keySet()) { + + for (DotName annotationName : annotationNames) { if (!additionalIndex.contains(annotationName) && quarkusIndex.getClassByName(annotationName) == null) { try (InputStream annotationStream = IoUtil.readClass(classLoader, annotationName.toString())) { log.debugf("Index annotation: %s", annotationName); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java index e1fa11ba794b9..acaf966faf7b0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java @@ -1,6 +1,5 @@ package io.quarkus.deployment.logging; -import java.lang.reflect.Modifier; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; @@ -38,7 +37,6 @@ import org.jboss.jandex.CompositeIndex; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; -import org.jboss.jandex.MethodInfo; import org.jboss.logging.Logger; import org.jboss.logmanager.EmbeddedConfigurator; import org.jboss.logmanager.LogManager; @@ -112,6 +110,7 @@ import io.quarkus.runtime.logging.LogBuildTimeConfig; import io.quarkus.runtime.logging.LogCleanupFilterElement; import io.quarkus.runtime.logging.LogConfig; +import io.quarkus.runtime.logging.LogFilterFactory; import io.quarkus.runtime.logging.LogMetricsHandlerRecorder; import io.quarkus.runtime.logging.LoggingSetupRecorder; @@ -126,7 +125,7 @@ public final class LoggingResourceProcessor { "isMinLevelEnabled", boolean.class, int.class, String.class); - private static final DotName LOGGING_FILTER = DotName.createSimple(LoggingFilter.class.getName()); + public static final DotName LOGGING_FILTER = DotName.createSimple(LoggingFilter.class.getName()); private static final DotName FILTER = DotName.createSimple(Filter.class.getName()); private static final String ILLEGAL_LOGGING_FILTER_USE_MESSAGE = "'@" + LoggingFilter.class.getName() + "' can only be used on classes that implement '" @@ -235,7 +234,8 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(RecorderContext context, LoggingSe BuildProducer shutdownListenerBuildItemBuildProducer, LaunchModeBuildItem launchModeBuildItem, List logCleanupFilters, - BuildProducer reflectiveClassBuildItemBuildProducer) { + BuildProducer reflectiveClassBuildItemBuildProducer, + BuildProducer serviceProviderBuildItemBuildProducer) { if (!launchModeBuildItem.isAuxiliaryApplication() || launchModeBuildItem.getAuxiliaryDevModeType().orElse(null) == DevModeType.TEST_ONLY) { final List>> handlers = handlerBuildItems.stream() @@ -272,6 +272,8 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(RecorderContext context, LoggingSe reflectiveClassBuildItemBuildProducer.produce(new ReflectiveClassBuildItem(true, false, false, discoveredLogComponents.getNameToFilterClass().values().toArray( EMPTY_STRING_ARRAY))); + serviceProviderBuildItemBuildProducer + .produce(ServiceProviderBuildItem.allProvidersFromClassPath(LogFilterFactory.class.getName())); } shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem( @@ -317,10 +319,6 @@ private DiscoveredLogComponents discoverLogComponents(IndexView index) { throw new IllegalStateException("Unimplemented mode of use of '" + LoggingFilter.class.getName() + "'"); } ClassInfo classInfo = target.asClass(); - if (!Modifier.isFinal(classInfo.flags())) { - throw new RuntimeException( - ILLEGAL_LOGGING_FILTER_USE_MESSAGE + " Offending class is '" + classInfo.name() + "'"); - } boolean isFilterImpl = false; ClassInfo currentClassInfo = classInfo; while ((currentClassInfo != null) && (!JandexUtil.DOTNAME_OBJECT.equals(currentClassInfo.name()))) { @@ -343,11 +341,6 @@ private DiscoveredLogComponents discoverLogComponents(IndexView index) { ILLEGAL_LOGGING_FILTER_USE_MESSAGE + " Offending class is '" + classInfo.name() + "'"); } - MethodInfo ctor = classInfo.method(""); - if ((ctor == null) || (ctor.typeParameters().size() > 0)) { - throw new RuntimeException("Classes annotated with '" + LoggingFilter.class.getName() - + "' must have a no-args constructor. Offending class is '" + classInfo.name() + "'"); - } String filterName = instance.value("name").asString(); if (filtersMap.containsKey(filterName)) { throw new RuntimeException("Filter '" + filterName + "' was defined multiple times."); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java index b63f2cb92886f..1be0521d44027 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java @@ -2,37 +2,112 @@ import static io.quarkus.deployment.pkg.steps.LinuxIDUtil.getLinuxID; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; import org.apache.commons.lang3.SystemUtils; +import org.jboss.logging.Logger; +import io.quarkus.deployment.OutputFilter; import io.quarkus.deployment.pkg.NativeConfig; +import io.quarkus.deployment.util.ExecUtil; import io.quarkus.deployment.util.FileUtil; import io.quarkus.runtime.util.ContainerRuntimeUtil; public class NativeImageBuildLocalContainerRunner extends NativeImageBuildContainerRunner { + private static final Logger LOGGER = Logger.getLogger(NativeImageBuildLocalContainerRunner.class.getName()); + public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outputDir) { super(nativeConfig, outputDir); if (SystemUtils.IS_OS_LINUX) { ArrayList containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs)); - String uid = getLinuxID("-ur"); - String gid = getLinuxID("-gr"); - if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) { - Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid); - if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) { - // Needed to avoid AccessDeniedExceptions - containerRuntimeArgs.add("--userns=keep-id"); + if (isDockerRootless(containerRuntime)) { + Collections.addAll(containerRuntimeArgs, "--user", String.valueOf(0)); + } else { + String uid = getLinuxID("-ur"); + String gid = getLinuxID("-gr"); + if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) { + Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid); + if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) { + // Needed to avoid AccessDeniedExceptions + containerRuntimeArgs.add("--userns=keep-id"); + } } } baseContainerRuntimeArgs = containerRuntimeArgs.toArray(baseContainerRuntimeArgs); } } + private static boolean isDockerRootless(ContainerRuntimeUtil.ContainerRuntime containerRuntime) { + if (containerRuntime != ContainerRuntimeUtil.ContainerRuntime.DOCKER) { + return false; + } + String dockerEndpoint = fetchDockerEndpoint(); + // docker socket? + String socketUriPrefix = "unix://"; + if (dockerEndpoint == null || !dockerEndpoint.startsWith(socketUriPrefix)) { + return false; + } + String dockerSocket = dockerEndpoint.substring(socketUriPrefix.length()); + String currentUid = getLinuxID("-ur"); + if (currentUid == null || currentUid.isEmpty() || currentUid.equals(String.valueOf(0))) { + return false; + } + + int socketOwnerUid; + try { + socketOwnerUid = (int) Files.getAttribute(Path.of(dockerSocket), "unix:uid", LinkOption.NOFOLLOW_LINKS); + } catch (IOException e) { + LOGGER.infof("Owner UID lookup on '%s' failed with '%s'", dockerSocket, e.getMessage()); + return false; + } + return currentUid.equals(String.valueOf(socketOwnerUid)); + } + + private static String fetchDockerEndpoint() { + // DOCKER_HOST environment variable overrides the active context + String dockerHost = System.getenv("DOCKER_HOST"); + if (dockerHost != null) { + return dockerHost; + } + + OutputFilter outputFilter = new OutputFilter(); + if (!ExecUtil.execWithTimeout(new File("."), outputFilter, Duration.ofMillis(3000), + "docker", "context", "ls", "--format", + "'{{- if .Current -}} {{- .DockerEndpoint -}} {{- end -}}'")) { + LOGGER.debug("Docker context lookup didn't succeed in time"); + return null; + } + + Set endpoints = outputFilter.getOutput().lines() + .filter(Objects::nonNull) + .filter(Predicate.not(String::isBlank)) + .collect(Collectors.toSet()); + if (endpoints.size() == 1) { + return endpoints.stream().findFirst().orElse(null); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debugf("Found too many active Docker endpoints: [%s]", + endpoints.stream() + .map(endpoint -> String.format("'%s'", endpoint)) + .collect(Collectors.joining(","))); + } + return null; + } + @Override protected List getContainerRuntimeBuildArgs() { List containerRuntimeArgs = super.getContainerRuntimeBuildArgs(); @@ -45,4 +120,5 @@ protected List getContainerRuntimeBuildArgs() { volumeOutputPath + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH + ":z"); return containerRuntimeArgs; } + } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/AnnotationProxyProvider.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/AnnotationProxyProvider.java index 4bb449bc7f7c0..f6960c54221a4 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/recording/AnnotationProxyProvider.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/AnnotationProxyProvider.java @@ -22,8 +22,8 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; import org.jboss.jandex.IndexView; -import org.jboss.jandex.Indexer; import org.jboss.jandex.MethodInfo; import io.quarkus.deployment.util.IoUtil; @@ -63,8 +63,7 @@ public AnnotationProxyBuilder builder(AnnotationInstan ClassInfo clazz = index.getClassByName(name); if (clazz == null) { try (InputStream annotationStream = IoUtil.readClass(classLoader, name.toString())) { - Indexer indexer = new Indexer(); - clazz = indexer.index(annotationStream); + clazz = Index.singleClass(annotationStream); } catch (Exception e) { throw new IllegalStateException("Failed to index: " + name, e); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ReflectiveHierarchyStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ReflectiveHierarchyStep.java index acb789975c268..ebcb59cdd0290 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ReflectiveHierarchyStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ReflectiveHierarchyStep.java @@ -22,6 +22,7 @@ import org.jboss.jandex.PrimitiveType; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; +import org.jboss.jandex.TypeVariableReference; import org.jboss.jandex.UnresolvedTypeVariable; import org.jboss.jandex.VoidType; import org.jboss.logging.Logger; @@ -133,7 +134,8 @@ private void addReflectiveHierarchy(CombinedIndexBuildItem combinedIndexBuildIte Deque visits) { if (type instanceof VoidType || type instanceof PrimitiveType || - type instanceof UnresolvedTypeVariable) { + type instanceof UnresolvedTypeVariable || + type instanceof TypeVariableReference) { return; } else if (type instanceof ClassType) { if (reflectiveHierarchyBuildItem.getIgnoreTypePredicate().test(type.name())) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java index 2deb8abaf8aea..d753f8c68408b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java @@ -28,6 +28,7 @@ import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; import org.jboss.jandex.TypeVariable; +import org.jboss.jandex.TypeVariableReference; import org.jboss.jandex.UnresolvedTypeVariable; import org.jboss.jandex.WildcardType; import org.objectweb.asm.MethodVisitor; @@ -385,6 +386,17 @@ private static void toSignature(StringBuilder sb, Type type, Function getWriteItems(); + + /** + * An item is a summary table, note below the table, ... + */ + interface WriteItem { + void accept(Writer writer) throws IOException; + } +} diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocBuilder.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocBuilder.java new file mode 100644 index 0000000000000..57edebb518529 --- /dev/null +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocBuilder.java @@ -0,0 +1,91 @@ +package io.quarkus.annotation.processor.generate_doc; + +import static io.quarkus.annotation.processor.Constants.SUMMARY_TABLE_ID_VARIABLE; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.annotation.processor.Constants; + +/** + * {@link ConfigDoc} builder + */ +class ConfigDocBuilder { + + /** + * Declare AsciiDoc variable + */ + private static final String DECLARE_VAR = "\n:%s: %s\n"; + private final DocFormatter summaryTableDocFormatter; + protected final List writeItems = new ArrayList<>(); + + public ConfigDocBuilder() { + summaryTableDocFormatter = new SummaryTableDocFormatter(); + } + + protected ConfigDocBuilder(boolean showEnvVars) { + summaryTableDocFormatter = new SummaryTableDocFormatter(showEnvVars); + } + + /** + * Add documentation in a summary table and descriptive format + */ + public final ConfigDocBuilder addSummaryTable(String initialAnchorPrefix, boolean activateSearch, + List configDocItems, String fileName, + boolean includeConfigPhaseLegend) { + + writeItems.add(writer -> { + + // Create var with unique value for each summary table that will make DURATION_FORMAT_NOTE (see below) unique + var fileNameWithoutExtension = fileName.substring(0, fileName.length() - Constants.ADOC_EXTENSION.length()); + writer.append(String.format(DECLARE_VAR, SUMMARY_TABLE_ID_VARIABLE, fileNameWithoutExtension)); + + summaryTableDocFormatter.format(writer, initialAnchorPrefix, activateSearch, configDocItems, + includeConfigPhaseLegend); + + boolean hasDuration = false, hasMemory = false; + for (ConfigDocItem item : configDocItems) { + if (item.hasDurationInformationNote()) { + hasDuration = true; + } + + if (item.hasMemoryInformationNote()) { + hasMemory = true; + } + } + + if (hasDuration) { + writer.append(Constants.DURATION_FORMAT_NOTE); + } + + if (hasMemory) { + writer.append(Constants.MEMORY_SIZE_FORMAT_NOTE); + } + }); + return this; + } + + public boolean hasWriteItems() { + return !writeItems.isEmpty(); + } + + /** + * Passed strings are appended to the file + */ + public final ConfigDocBuilder write(String... strings) { + requireNonNull(strings); + writeItems.add(writer -> { + for (String str : strings) { + writer.append(str); + } + }); + return this; + } + + public final ConfigDoc build() { + final List docItemsCopy = List.copyOf(writeItems); + return () -> docItemsCopy; + } + +} diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocWriter.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocWriter.java index d7d99741ef315..b2baf426709a3 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocWriter.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocWriter.java @@ -1,65 +1,47 @@ package io.quarkus.annotation.processor.generate_doc; -import static io.quarkus.annotation.processor.Constants.SUMMARY_TABLE_ID_VARIABLE; - import java.io.IOException; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import io.quarkus.annotation.processor.Constants; final public class ConfigDocWriter { - private final DocFormatter summaryTableDocFormatter = new SummaryTableDocFormatter(); - private static final String DECLARE_VAR = "\n:%s: %s\n"; /** * Write all extension configuration in AsciiDoc format in `{root}/target/asciidoc/generated/config/` directory */ public void writeAllExtensionConfigDocumentation(ConfigDocGeneratedOutput output) throws IOException { - generateDocumentation(Constants.GENERATED_DOCS_PATH.resolve(output.getFileName()), output.getAnchorPrefix(), - output.isSearchable(), output.getConfigDocItems(), output.getFileName()); - } - /** - * Generate documentation in a summary table and descriptive format - * - */ - private void generateDocumentation(Path targetPath, String initialAnchorPrefix, boolean activateSearch, - List configDocItems, String fileName) - throws IOException { - if (configDocItems.isEmpty()) { + if (output.getConfigDocItems().isEmpty()) { return; } - try (Writer writer = Files.newBufferedWriter(targetPath)) { - - // Create var with unique value for each summary table that will make DURATION_FORMAT_NOTE (see below) unique - var fileNameWithoutExtension = fileName.substring(0, fileName.length() - Constants.ADOC_EXTENSION.length()); - writer.append(String.format(DECLARE_VAR, SUMMARY_TABLE_ID_VARIABLE, fileNameWithoutExtension)); - - summaryTableDocFormatter.format(writer, initialAnchorPrefix, activateSearch, configDocItems); + // Create single summary table + final var configDocBuilder = new ConfigDocBuilder().addSummaryTable(output.getAnchorPrefix(), output.isSearchable(), + output.getConfigDocItems(), output.getFileName(), true); - boolean hasDuration = false, hasMemory = false; - for (ConfigDocItem item : configDocItems) { - if (item.hasDurationInformationNote()) { - hasDuration = true; - } - - if (item.hasMemoryInformationNote()) { - hasMemory = true; - } - } + generateDocumentation(output.getFileName(), configDocBuilder); + } - if (hasDuration) { - writer.append(Constants.DURATION_FORMAT_NOTE); - } + public void generateDocumentation(String fileName, ConfigDocBuilder configDocBuilder) throws IOException { + generateDocumentation( + // Resolve output file path + Constants.GENERATED_DOCS_PATH.resolve(fileName), + // Write all items + configDocBuilder.build()); + } - if (hasMemory) { - writer.append(Constants.MEMORY_SIZE_FORMAT_NOTE); + private void generateDocumentation(Path targetPath, ConfigDoc configDoc) + throws IOException { + try (Writer writer = Files.newBufferedWriter(targetPath)) { + for (ConfigDoc.WriteItem writeItem : configDoc.getWriteItems()) { + // Write documentation item, f.e. summary table + writeItem.accept(writer); } } } + } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocFormatter.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocFormatter.java index b33fe84401bb1..0564a820dbfc7 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocFormatter.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocFormatter.java @@ -61,8 +61,8 @@ default String getAnchor(String string) { return string.toLowerCase(); } - void format(Writer writer, String initialAnchorPrefix, boolean activateSearch, List configDocItems) - throws IOException; + void format(Writer writer, String initialAnchorPrefix, boolean activateSearch, List configDocItems, + boolean includeConfigPhaseLegend) throws IOException; void format(Writer writer, ConfigDocKey configDocKey) throws IOException; diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java index 756f34b97c105..c0afad96a5ab2 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java @@ -24,6 +24,7 @@ final class JavaDocParser { private static final Pattern START_OF_LINE = Pattern.compile("^", Pattern.MULTILINE); private static final Pattern REPLACE_WINDOWS_EOL = Pattern.compile("\r\n"); private static final Pattern REPLACE_MACOS_EOL = Pattern.compile("\r"); + private static final Pattern STARTING_SPACE = Pattern.compile("^ +"); private static final String BACKTICK = "`"; private static final String HASH = "#"; @@ -269,7 +270,21 @@ private void appendHtml(StringBuilder sb, Node node) { sb.append(NEW_LINE); break; case TEXT_NODE: - appendEscapedAsciiDoc(sb, ((TextNode) childNode).text()); + String text = ((TextNode) childNode).text(); + + if (text.isEmpty()) { + break; + } + + // Indenting the first line of a paragraph by one or more spaces makes the block literal + // Please see https://docs.asciidoctor.org/asciidoc/latest/verbatim/literal-blocks/ for more info + // This prevents literal blocks f.e. after
+ final var startingSpaceMatcher = STARTING_SPACE.matcher(text); + if (sb.length() > 0 && '\n' == sb.charAt(sb.length() - 1) && startingSpaceMatcher.find()) { + text = startingSpaceMatcher.replaceFirst(""); + } + + appendEscapedAsciiDoc(sb, text); break; default: appendHtml(sb, childNode); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/MavenConfigDocBuilder.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/MavenConfigDocBuilder.java new file mode 100644 index 0000000000000..e55a495fbc1a3 --- /dev/null +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/MavenConfigDocBuilder.java @@ -0,0 +1,98 @@ +package io.quarkus.annotation.processor.generate_doc; + +import static io.quarkus.annotation.processor.Constants.EMPTY; +import static io.quarkus.annotation.processor.Constants.NEW_LINE; +import static io.quarkus.annotation.processor.Constants.SECTION_TITLE_L1; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.annotation.processor.Constants; + +public final class MavenConfigDocBuilder extends ConfigDocBuilder { + + public MavenConfigDocBuilder() { + super(false); + } + + private final JavaDocParser javaDocParser = new JavaDocParser(); + + public void addTableTitle(String goalTitle) { + write(SECTION_TITLE_L1, goalTitle, NEW_LINE); + } + + public void addNewLine() { + write(NEW_LINE); + } + + public void addTableDescription(String goalDescription) { + write(NEW_LINE, javaDocParser.parseConfigDescription(goalDescription), NEW_LINE); + } + + public GoalParamsBuilder newGoalParamsBuilder() { + return new GoalParamsBuilder(javaDocParser); + } + + private static abstract class TableBuilder { + + protected final List configDocItems = new ArrayList<>(); + + /** + * Section name that is displayed in a table header + */ + abstract protected String getSectionName(); + + public List build() { + + // a summary table + final ConfigDocSection parameterSection = new ConfigDocSection(); + parameterSection.setShowSection(true); + parameterSection.setName(getSectionName()); + parameterSection.setSectionDetailsTitle(getSectionName()); + parameterSection.setOptional(false); + parameterSection.setConfigDocItems(List.copyOf(configDocItems)); + + // topConfigDocItem wraps the summary table + final ConfigDocItem topConfigDocItem = new ConfigDocItem(); + topConfigDocItem.setConfigDocSection(parameterSection); + + return List.of(topConfigDocItem); + } + + public boolean tableIsNotEmpty() { + return !configDocItems.isEmpty(); + } + } + + public static final class GoalParamsBuilder extends TableBuilder { + + private final JavaDocParser javaDocParser; + + private GoalParamsBuilder(JavaDocParser javaDocParser) { + this.javaDocParser = javaDocParser; + } + + public void addParam(String type, String name, String defaultValue, boolean required, String description) { + final ConfigDocKey configDocKey = new ConfigDocKey(); + configDocKey.setType(type); + configDocKey.setKey(name); + configDocKey.setConfigPhase(ConfigPhase.RUN_TIME); + configDocKey.setDefaultValue(defaultValue == null ? Constants.EMPTY : defaultValue); + if (description != null && !description.isBlank()) { + configDocKey.setConfigDoc(javaDocParser.parseConfigDescription(description)); + } else { + configDocKey.setConfigDoc(EMPTY); + } + configDocKey.setOptional(!required); + final ConfigDocItem configDocItem = new ConfigDocItem(); + configDocItem.setConfigDocKey(configDocKey); + configDocItems.add(configDocItem); + } + + @Override + protected String getSectionName() { + return "Parameter"; + } + } + +} diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/SummaryTableDocFormatter.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/SummaryTableDocFormatter.java index 47935389f9c42..654b484967c47 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/SummaryTableDocFormatter.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/SummaryTableDocFormatter.java @@ -1,5 +1,7 @@ package io.quarkus.annotation.processor.generate_doc; +import static io.quarkus.annotation.processor.Constants.CONFIG_PHASE_LEGEND; +import static io.quarkus.annotation.processor.Constants.NEW_LINE; import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.toEnvVarName; import java.io.IOException; @@ -15,22 +17,34 @@ final class SummaryTableDocFormatter implements DocFormatter { public static final String CONFIGURATION_TABLE_CLASS = ".configuration-reference"; private static final String TABLE_ROW_FORMAT = "\n\na|%s [[%s]]`link:#%s[%s]`\n\n[.description]\n--\n%s\n--%s|%s %s\n|%s\n"; private static final String SECTION_TITLE = "[[%s]]link:#%s[%s]"; + private static final String TABLE_HEADER_FORMAT = "[%s, cols=\"80,.^10,.^10\"]\n|==="; private static final String TABLE_SECTION_ROW_FORMAT = "\n\nh|%s\n%s\nh|Type\nh|Default"; - private static final String TABLE_HEADER_FORMAT = "[.configuration-legend]%s\n[%s, cols=\"80,.^10,.^10\"]\n|==="; + private final boolean showEnvVars; private String anchorPrefix = ""; + public SummaryTableDocFormatter(boolean showEnvVars) { + this.showEnvVars = showEnvVars; + } + + public SummaryTableDocFormatter() { + this(true); + } + /** * Generate configuration keys in table format with search engine activated or not. * Useful when we want to optionally activate or deactivate search engine */ @Override - public void format(Writer writer, String initialAnchorPrefix, boolean activateSearch, List configDocItems) + public void format(Writer writer, String initialAnchorPrefix, boolean activateSearch, + List configDocItems, boolean includeConfigPhaseLegend) throws IOException { + if (includeConfigPhaseLegend) { + writer.append("[.configuration-legend]").append(CONFIG_PHASE_LEGEND).append(NEW_LINE); + } String searchableClass = activateSearch ? SEARCHABLE_TABLE_CLASS : Constants.EMPTY; String tableClasses = CONFIGURATION_TABLE_CLASS + searchableClass; - final String tableHeaders = String.format(TABLE_HEADER_FORMAT, Constants.CONFIG_PHASE_LEGEND, tableClasses); - writer.append(tableHeaders); + writer.append(String.format(TABLE_HEADER_FORMAT, tableClasses)); anchorPrefix = initialAnchorPrefix; // make sure that section-less configs get a legend @@ -74,18 +88,20 @@ public void format(Writer writer, ConfigDocKey configDocKey) throws IOException String doc = configDocKey.getConfigDoc(); - // Convert a property name to an environment variable name and show it in the config description - final String envVarExample = String.format("ifdef::add-copy-button-to-env-var[]\n" + - "Environment variable: env_var_with_copy_button:+++%1$s+++[]\n" + - "endif::add-copy-button-to-env-var[]\n" + - "ifndef::add-copy-button-to-env-var[]\n" + - "Environment variable: `+++%1$s+++`\n" + - "endif::add-copy-button-to-env-var[]", toEnvVarName(configDocKey.getKey())); - if (configDocKey.getConfigDoc().isEmpty()) { - doc = envVarExample; - } else { - // Add 2 new lines in order to show the environment variable on next line - doc += TWO_NEW_LINES + envVarExample; + if (showEnvVars) { + // Convert a property name to an environment variable name and show it in the config description + final String envVarExample = String.format("ifdef::add-copy-button-to-env-var[]\n" + + "Environment variable: env_var_with_copy_button:+++%1$s+++[]\n" + + "endif::add-copy-button-to-env-var[]\n" + + "ifndef::add-copy-button-to-env-var[]\n" + + "Environment variable: `+++%1$s+++`\n" + + "endif::add-copy-button-to-env-var[]", toEnvVarName(configDocKey.getKey())); + if (configDocKey.getConfigDoc().isEmpty()) { + doc = envVarExample; + } else { + // Add 2 new lines in order to show the environment variable on next line + doc += TWO_NEW_LINES + envVarExample; + } } final String typeDetail = DocGeneratorUtil.getTypeFormatInformationNote(configDocKey); diff --git a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java index 292162160a273..b84268727d097 100644 --- a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java +++ b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java @@ -25,6 +25,12 @@ public void parseNullJavaDoc() { assertEquals("", parsed); } + @Test + public void removeParagraphIndentation() { + String parsed = parser.parseConfigDescription("First paragraph

Second Paragraph"); + assertEquals("First paragraph\n\nSecond Paragraph", parsed); + } + @Test public void parseUntrimmedJavaDoc() { String parsed = parser.parseConfigDescription(" "); diff --git a/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java b/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java index bf2adb61f2b4f..4f07aaa0afe46 100644 --- a/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java +++ b/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java @@ -8,8 +8,6 @@ /** * Makes the filter class known to Quarkus by the specified name. * The filter can then be configured for a handler (like the logging handler using {@code quarkus.log.console.filter}). - * - * This class must ONLY be placed on implementations of {@link java.util.logging.Filter} that are marked as {@code final}. */ @Retention(RetentionPolicy.RUNTIME) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogFilterFactory.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogFilterFactory.java new file mode 100644 index 0000000000000..d94d67907423d --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogFilterFactory.java @@ -0,0 +1,57 @@ +package io.quarkus.runtime.logging; + +import java.util.ServiceLoader; +import java.util.logging.Filter; + +/** + * Factory that allows for the creation of {@link Filter} classes annotated with {@link io.quarkus.logging.LoggingFilter}. + * Implementations of this class are loaded via the {@link ServiceLoader} and the implementation selected is the one + * with the lowest value returned from the {@code priority} method. + */ +public interface LogFilterFactory { + + int MIN_PRIORITY = Integer.MAX_VALUE; + int DEFAULT_PRIORITY = 0; + + Filter create(String className) throws Exception; + + default int priority() { + return DEFAULT_PRIORITY; + } + + static LogFilterFactory load() { + LogFilterFactory result = null; + ServiceLoader load = ServiceLoader.load(LogFilterFactory.class); + for (LogFilterFactory next : load) { + if (result == null) { + result = next; + } else { + if (next.priority() < result.priority()) { + result = next; + } + } + } + if (result == null) { + result = new ReflectionLogFilterFactory(); + } + return result; + } + + /** + * The default implementation used when no other implementation is found. + * This simply calls the class' no-arg constructor (and fails if one does not exist). + */ + class ReflectionLogFilterFactory implements LogFilterFactory { + + @Override + public Filter create(String className) throws Exception { + return (Filter) Class.forName(className, true, Thread.currentThread().getContextClassLoader()) + .getConstructor().newInstance(); + } + + @Override + public int priority() { + return LogFilterFactory.MIN_PRIORITY; + } + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index f10f9c3dab731..4d2d143e22dbf 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -55,7 +55,6 @@ public class LoggingSetupRecorder { private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LoggingSetupRecorder.class); - public static final String SHUTDOWN_MESSAGE = " [Error Occurred After Shutdown]"; final RuntimeValue consoleRuntimeConfig; @@ -254,13 +253,12 @@ private static Map createNamedFilters(DiscoveredLogComponents di } Map nameToFilter = new HashMap<>(); + LogFilterFactory logFilterFactory = LogFilterFactory.load(); discoveredLogComponents.getNameToFilterClass().forEach(new BiConsumer<>() { @Override public void accept(String name, String className) { try { - nameToFilter.put(name, - (Filter) Class.forName(className, true, Thread.currentThread().getContextClassLoader()) - .getConstructor().newInstance()); + nameToFilter.put(name, logFilterFactory.create(className)); } catch (Exception e) { throw new RuntimeException("Unable to create instance of Logging Filter '" + className + "'"); } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index eb5037023d6d3..bfd2968ee22dc 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -170,6 +170,7 @@ public class DevMojo extends AbstractMojo { private static final String ORG_JETBRAINS_KOTLIN = "org.jetbrains.kotlin"; private static final String KOTLIN_MAVEN_PLUGIN = "kotlin-maven-plugin"; + private static final String IO_SMALLRYE = "io.smallrye"; private static final String ORG_JBOSS_JANDEX = "org.jboss.jandex"; private static final String JANDEX_MAVEN_PLUGIN = "jandex-maven-plugin"; @@ -513,8 +514,9 @@ private void handleAutoCompile() throws MojoExecutionException { boolean prepareNeeded = true; boolean prepareTestsNeeded = true; - String jandexGoalPhase = getGoalPhaseOrNull(ORG_JBOSS_JANDEX, JANDEX_MAVEN_PLUGIN, "jandex", "process-classes"); - boolean indexClassNeeded = jandexGoalPhase != null; + String jandexGoalPhase = getGoalPhaseOrNull(IO_SMALLRYE, JANDEX_MAVEN_PLUGIN, "jandex", "process-classes"); + String legacyJandexGoalPhase = getGoalPhaseOrNull(ORG_JBOSS_JANDEX, JANDEX_MAVEN_PLUGIN, "jandex", "process-classes"); + boolean indexClassNeeded = legacyJandexGoalPhase != null || jandexGoalPhase != null; List goals = session.getGoals(); // check for default goal(s) if none were specified explicitly, @@ -538,6 +540,10 @@ private void handleAutoCompile() throws MojoExecutionException { && POST_COMPILE_PHASES.indexOf(goal) >= POST_COMPILE_PHASES.indexOf(jandexGoalPhase)) { indexClassNeeded = false; } + if (jandexGoalPhase == null && legacyJandexGoalPhase != null + && POST_COMPILE_PHASES.indexOf(goal) >= POST_COMPILE_PHASES.indexOf(legacyJandexGoalPhase)) { + indexClassNeeded = false; + } if (POST_TEST_COMPILE_PHASES.contains(goal)) { testCompileNeeded = false; @@ -552,7 +558,7 @@ private void handleAutoCompile() throws MojoExecutionException { triggerCompile(false, prepareNeeded); } if (indexClassNeeded) { - initClassIndexes(); + initClassIndexes(jandexGoalPhase == null); } if (testCompileNeeded) { try { @@ -574,8 +580,12 @@ private void triggerPrepare(boolean test) throws MojoExecutionException { Map.of("mode", LaunchMode.DEVELOPMENT.name(), QuarkusBootstrapMojo.CLOSE_BOOTSTRAPPED_APP, "false")); } - private void initClassIndexes() throws MojoExecutionException { - executeIfConfigured(ORG_JBOSS_JANDEX, JANDEX_MAVEN_PLUGIN, "jandex", Collections.emptyMap()); + private void initClassIndexes(boolean legacyJandex) throws MojoExecutionException { + if (legacyJandex) { + executeIfConfigured(ORG_JBOSS_JANDEX, JANDEX_MAVEN_PLUGIN, "jandex", Collections.emptyMap()); + } else { + executeIfConfigured(IO_SMALLRYE, JANDEX_MAVEN_PLUGIN, "jandex", Collections.emptyMap()); + } } private PluginDescriptor getPluginDescriptor() { diff --git a/docs/pom.xml b/docs/pom.xml index 9d822f26545c2..d6f3ee24e9783 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -2802,6 +2802,23 @@ + + generate-quarkus-mvn-plugin-docs + process-classes + + java + + + ${skipDocs} + io.quarkus.docs.generation.QuarkusMavenPluginDocsGenerator + + ${project.basedir}/../devtools/maven/target/classes/META-INF/maven/plugin.xml + + + ${env.MAVEN_CMD_LINE_ARGS} + + + all-build-item-classes process-classes @@ -2901,7 +2918,7 @@ ${kibana.image} ${keycloak.docker.image} - ${jandex-maven-plugin.version} + ${jandex.version} ${jandex-gradle-plugin.version} ${kotlin.version} diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index b98af78afd3b7..43b67fae2490a 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -54,9 +54,9 @@ To generate the index just add the following plugin to your build file: - org.jboss.jandex + io.smallrye jandex-maven-plugin - {jandex-maven-plugin-version} + {jandex-version} make-index diff --git a/docs/src/main/asciidoc/grpc-getting-started.adoc b/docs/src/main/asciidoc/grpc-getting-started.adoc index a1ec29a3c515f..353a0360c56aa 100644 --- a/docs/src/main/asciidoc/grpc-getting-started.adoc +++ b/docs/src/main/asciidoc/grpc-getting-started.adoc @@ -411,9 +411,9 @@ The `jandex-maven-plugin` can be used to create a Jandex index. More information - org.jboss.jandex + io.smallrye jandex-maven-plugin - {jandex-maven-plugin-version} + {jandex-version} make-index diff --git a/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-1.png b/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-1.png new file mode 100644 index 0000000000000..13271519264dc Binary files /dev/null and b/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-1.png differ diff --git a/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-2.png b/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-2.png new file mode 100644 index 0000000000000..ea8cfdeed41d4 Binary files /dev/null and b/docs/src/main/asciidoc/images/security-bearer-token-authorization-mechanism-2.png differ diff --git a/docs/src/main/asciidoc/logging.adoc b/docs/src/main/asciidoc/logging.adoc index f946ff505ef15..3a5cb4a1cec3f 100644 --- a/docs/src/main/asciidoc/logging.adoc +++ b/docs/src/main/asciidoc/logging.adoc @@ -325,7 +325,8 @@ These filters are registered by placing the `@io.quarkus.logging.LoggingFilter` Finally, the filter is attached using the `filter` configuration property of the appropriate handler. -Let's say for example that we wanted to filter out logging records that contained the word `test` from the console logs. +Let's say for example that we wanted to filter out logging records that contained a part of text from the console logs. +The text itself is part of the application configuration and is not hardcoded. We could write a filter like so: [source,java] @@ -336,13 +337,28 @@ import java.util.logging.LogRecord; @LoggingFilter(name = "my-filter") public final class TestFilter implements Filter { + + private final String part; + + public TestFilter(@ConfigProperty(name = "my-filter.part") String part) { + this.part = part; + } + @Override public boolean isLoggable(LogRecord record) { - return !record.getMessage().contains("test"); + return !record.getMessage().contains(part); } } ---- +and configure it in the usual Quarkus way (for example using `application.properties`) like so: + +[source,properties] +---- +my-filter.part=TEST +---- + + And we would register this filter to the console handler like so: [source, properties] diff --git a/docs/src/main/asciidoc/mailer-reference.adoc b/docs/src/main/asciidoc/mailer-reference.adoc index 67dff0bdfffcf..2aa9e6342d6f6 100644 --- a/docs/src/main/asciidoc/mailer-reference.adoc +++ b/docs/src/main/asciidoc/mailer-reference.adoc @@ -368,6 +368,8 @@ quarkus.mailer.port=587 quarkus.mailer.start-tls=REQUIRED quarkus.mailer.username=YOUREMAIL@gmail.com quarkus.mailer.password=YOURGENERATEDAPPLICATIONPASSWORD + +quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server ---- Or with SSL: @@ -381,6 +383,8 @@ quarkus.mailer.port=465 quarkus.mailer.ssl=true quarkus.mailer.username=YOUREMAIL@gmail.com quarkus.mailer.password=YOURGENERATEDAPPLICATIONPASSWORD + +quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server ---- [NOTE] diff --git a/docs/src/main/asciidoc/mailer.adoc b/docs/src/main/asciidoc/mailer.adoc index 882d096f1bcd9..e85271a91c4fa 100644 --- a/docs/src/main/asciidoc/mailer.adoc +++ b/docs/src/main/asciidoc/mailer.adoc @@ -181,32 +181,10 @@ In the `src/main/resources/application.properties` file, you need to configure t Note that the password can also be configured using system properties and environment variables. See the xref:config-reference.adoc[configuration reference guide] for details. -Here is an example using _sendgrid_: - -[source,properties] ----- -# Your email address you send from - must match the "from" address from sendgrid. -quarkus.mailer.from=test@quarkus.io - -# The SMTP host -quarkus.mailer.host=smtp.sendgrid.net -# The SMTP port -quarkus.mailer.port=465 -# If the SMTP connection requires SSL/TLS -quarkus.mailer.ssl=true -# Your username -quarkus.mailer.username=.... -# Your password -quarkus.mailer.password=.... - -# By default, in dev mode, the mailer is a mock. This disables the mock and use the configured mailer. -quarkus.mailer.mock=false ----- +Configuration of popular mail services is covered in xref:mailer-reference.adoc#popular[the reference guide]. Once you have configured the mailer, if you call the HTTP endpoint as shown above, you will send emails. -Other popular mail services are covered in xref:mailer-reference.adoc#popular[the reference guide]. - == Conclusion This guide has shown how to send emails from your Quarkus application. diff --git a/docs/src/main/asciidoc/maven-tooling.adoc b/docs/src/main/asciidoc/maven-tooling.adoc index c0e4da989f6ba..75fadb6958bc8 100644 --- a/docs/src/main/asciidoc/maven-tooling.adoc +++ b/docs/src/main/asciidoc/maven-tooling.adoc @@ -590,9 +590,9 @@ unless it is the main application module already configured with the quarkus-mav - org.jboss.jandex + io.smallrye jandex-maven-plugin - {jandex-maven-plugin-version} + {jandex-version} make-index diff --git a/docs/src/main/asciidoc/quarkus-maven-plugin.adoc b/docs/src/main/asciidoc/quarkus-maven-plugin.adoc new file mode 100644 index 0000000000000..249c48287672c --- /dev/null +++ b/docs/src/main/asciidoc/quarkus-maven-plugin.adoc @@ -0,0 +1,27 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Quarkus Maven Plugin + +The Quarkus Maven Plugin builds the Quarkus applications, and provides helpers to launch dev mode or build native executables. +For more information about how to use the Quarkus Maven Plugin, please refer to the xref:maven-tooling.adoc[Maven Tooling guide]. + +include::./attributes.adoc[] + +== Discover Maven goals + +Like most Maven plugins, the Quarkus Maven Plugin has a `help` goal that prints the description of the plugin, listing all available goals as well as their description. +It is also possible to print out detailed information about a goal, all its parameters and their default values. For instance, to see the help for the `create` goal, run: + +[source,shell] +---- +./mvnw quarkus:help -Ddetail -Dgoal=create +---- + +== Maven goals reference + +Here is the list of all the Quarkus Maven Plugin goals: + +include::{generated-dir}/config/quarkus-maven-plugin-goals.adoc[opts=optional, leveloffset=+2] diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index 6a50ddec75fe8..cd74be8f916f6 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -632,9 +632,9 @@ It's also possible to access the iteration metadata inside the loop via the foll * `hasNext` - `true` if the iteration has more elements * `isLast` - `true` if `hasNext == false` * `isFirst` - `true` if `count == 1` -* `odd` - `true` if the zero-based index is odd -* `even` - `true` if the zero-based index is even -* `indexParity` - outputs `odd` or `even` based on the zero-based index value +* `odd` - `true` if the element's count is odd +* `even` - `true` if the element's count is even +* `indexParity` - outputs `odd` or `even` based on the count value However, the keys cannot be used directly. Instead, a prefix is used to avoid possible collisions with variables from the outer scope. @@ -679,7 +679,7 @@ The `for` statement also works with integers, starting from 1. In the example be [source] ---- {#for i in total} - {i}: + {i}: ({i_count} {i_indexParity} {i_even})
{/for} ---- @@ -687,7 +687,9 @@ And the output will be: [source] ---- -1:2:3: +1: (1 odd false) +2: (2 even true) +3: (3 odd false) ---- A loop section may also define the `{#else}` block that is executed when there are no items to iterate: diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index d3130db9ecb9c..456090e604ee2 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -84,6 +84,8 @@ In some cases such a default logic of selecting the challenge is exactly what is [source,java] ---- +@Alternative <1> +@Priority(1) @ApplicationScoped public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism { @@ -102,18 +104,21 @@ public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism @Override public Uni getChallenge(RoutingContext context) { - return selectBetweenJwtAndOidcChallenge(context).getChallenge(context); + return selectBetweenJwtAndOidcChallenge(context).getChallenge(context); } @Override public Set> getCredentialTypes() { - return selectBetweenJwtAndOidc(context).getCredentialTypes(); + Set> credentialTypes = new HashSet<>(); + credentialTypes.addAll(jwt.getCredentialTypes()); + credentialTypes.addAll(oidc.getCredentialTypes()); + return credentialTypes; } - @Override - public HttpCredentialTransport getCredentialTransport(RoutingContext context) { - return selectBetweenJwtAndOidc(context).getCredentialTransport(); - } + @Override + public Uni getCredentialTransport(RoutingContext context) { + return selectBetweenJwtAndOidc(context).getCredentialTransport(context); + } private HttpAuthenticationMechanism selectBetweenJwtAndOidc(RoutingContext context) { .... @@ -125,6 +130,7 @@ public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism } ---- +<1> Declaring the mechanism an alternative bean ensures this mechanism is used rather than `OidcAuthenticationMechanism` and `JWTAuthMechanism`. [[security-identity-customization]] == Security Identity Customization diff --git a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc index 7f5a410e8c0c0..559905d7aad4c 100644 --- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc @@ -15,8 +15,7 @@ After authentication, the OIDC provider redirects the user back to the applicati The following diagram outlines the Authorization Code Flow mechanism in Quarkus. .Authorization code flow mechanism in Quarkus -image::authorization_code_flow.png[alt=Authorization Code Flow, align=center] - +image::authorization_code_flow.png[alt=Authorization Code Flow, role="center"] . The Quarkus user requests access to a Quarkus web-app application. . The Quarkus web-app redirects the user to the authorization endpoint, that is, the OIDC provider for authentication. diff --git a/docs/src/main/asciidoc/security-openid-connect.adoc b/docs/src/main/asciidoc/security-openid-connect.adoc index 0b03187201eb9..af0f39765b706 100644 --- a/docs/src/main/asciidoc/security-openid-connect.adoc +++ b/docs/src/main/asciidoc/security-openid-connect.adoc @@ -8,15 +8,37 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] :toc: -This guide demonstrates how to use Quarkus OpenID Connect (OIDC) Extension to protect your JAX-RS applications using Bearer Token Authorization where Bearer Tokens are issued by OpenID Connect and OAuth 2.0 compliant Authorization Servers such as https://www.keycloak.org[Keycloak]. +You can use the Quarkus OpenID Connect (OIDC) extension to secure your JAX-RS applications using Bearer Token Authorization. +The Bearer Tokens are issued by OIDC and OAuth 2.0 compliant authorization servers, such as https://www.keycloak.org[Keycloak]. -Bearer Token Authorization is the process of authorizing HTTP requests based on the existence and validity of a Bearer Token which provides valuable information to determine the subject of the call as well as whether an HTTP resource can be accessed. +Bearer Token Authorization is the process of authorizing HTTP requests based on the existence and validity of a Bearer Token. +The Bearer Token provides information about the subject of the call which is used to determine whether or not an HTTP resource can be accessed. -Please read the xref:security-openid-connect-web-authentication.adoc[Using OpenID Connect to Protect Web Applications] guide if you need to authenticate and authorize the users using OpenID Connect Authorization Code Flow. +The following diagrams outline the Bearer Token Authorization mechanism in Quarkus: -If you use Keycloak and Bearer tokens then also see the xref:security-keycloak-authorization.adoc[Using Keycloak to Centralize Authorization] guide. +.Bearer Token Authorization mechanism in Quarkus with Single-page application +image::security-bearer-token-authorization-mechanism-1.png[alt=Bearer Token Authorization, width="60%", align=center] + +1. The Quarkus service retrieves verification keys from the OpenID Connect provider. The verification keys are used to verify the bearer access token signatures. +2. The Quarkus user accesses the Single-page application. +3. The Single-page application uses Authorization Code Flow to authenticate the user and retrieve tokens from the OpenID Connect provider. +4. The Single-page application uses the access token to retrieve the service data from the Quarkus service. +5. The Quarkus service verifies the bearer access token signature using the verification keys, checks the token expiry date and other claims, allows the request to proceed if the token is valid, and returns the service response to the Single-page application. +6. The Single-page application returns the same data to the Quarkus user. + +.Bearer Token Authorization mechanism in Quarkus with Java or command line client +image::security-bearer-token-authorization-mechanism-2.png[alt=Bearer Token Authorization, width="60%", align=center] + +1. The Quarkus service retrieves verification keys from the OpenID Connect provider. The verification keys are used to verify the bearer access token signatures. +2. The Client uses `client_credentials` that requires client ID and secret or password grant, which also requires client ID, secret, user name, and password to retrieve the access token from the OpenID Connect provider. +3. The Client uses the access token to retrieve the service data from the Quarkus service. +4. The Quarkus service verifies the bearer access token signature using the verification keys, checks the token expiry date and other claims, allows the request to proceed if the token is valid, and returns the service response to the Client. + +If you need to authenticate and authorize the users using OpenID Connect Authorization Code Flow, see xref:security-openid-connect-web-authentication.adoc[Using OpenID Connect to Protect Web Applications]. +Also, if you use Keycloak and Bearer Tokens, see xref:security-keycloak-authorization.adoc[Using Keycloak to Centralize Authorization]. + +For information about how to support multiple tenants, see xref:security-openid-connect-multitenancy.adoc[Using OpenID Connect Multi-Tenancy]. -Please read the xref:security-openid-connect-multitenancy.adoc[Using OpenID Connect Multi-Tenancy] guide how to support multiple tenants. == Quickstart diff --git a/docs/src/main/asciidoc/virtual-threads.adoc b/docs/src/main/asciidoc/virtual-threads.adoc new file mode 100644 index 0000000000000..e55f24476308e --- /dev/null +++ b/docs/src/main/asciidoc/virtual-threads.adoc @@ -0,0 +1,521 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Writing simpler reactive REST services with Quarkus Virtual Thread support + +include::./attributes.adoc[] +:resteasy-reactive-api: https://javadoc.io/doc/io.quarkus.resteasy.reactive/resteasy-reactive/{quarkus-version} +:resteasy-reactive-common-api: https://javadoc.io/doc/io.quarkus.resteasy.reactive/resteasy-reactive-common/{quarkus-version} +:runonvthread: https://javadoc.io/doc/io.smallrye.common/smallrye-common-annotation/latest/io/smallrye/common/annotation/RunOnVirtualThread.html +:blockingannotation: https://javadoc.io/doc/io.smallrye.common/smallrye-common-annotation/latest/io/smallrye/common/annotation/Blocking.html +:vthreadjep: https://openjdk.org/jeps/425 +:thread: https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Thread.html +:mutiny-vertx-sql: https://smallrye.io/smallrye-mutiny-vertx-bindings/2.26.0/apidocs/io/vertx/mutiny/sqlclient/package-summary.html +:pgsql-driver: https://javadoc.io/doc/org.postgresql/postgresql/latest/index.html + +This guide explains how to benefit from Java 19 virtual threads when writing REST services in Quarkus. + +[TIP] +==== +This is the reference guide for using virtual threads to write reactive REST services. +Please refer to the xref:rest-json.adoc[Writing JSON REST services guides] for a lightweight introduction to reactive REST +services and to the xref:resteasy-reactive.adoc[Writing REST Services with RESTEasy Reactive] guide for a detailed presentation. +==== + +== What are virtual threads ? + +=== Terminology +OS thread:: +A "thread-like" data-structure managed by the Operating System. + +Platform thread:: +Up until Java 19, every instance of the link:{thread}[Thread] class was a platform thread, that is, a wrapper around an OS thread. +Creating a platform threads creates an OS thread, blocking a platform thread blocks an OS thread. + +Virtual thread:: +Lightweight, JVM-managed threads. They extend the link:{thread}[Thread] class but are not tied to one specific OS thread. +Thus, scheduling virtual threads is the responsibility of the JVM. + +Carrier thread:: +A platform thread used to execute a virtual thread is called a carrier. +This isn't a class distinct from link:{Thread}[Thread] or VirtualThread but rather a functional denomination. + +=== Differences between virtual threads and platform threads +We will give a brief overview of the topic here, please refer to the link:{vthreadjep}[JEP 425] for more information. + +Virtual threads are a feature available since Java 19 aiming at providing a cheap alternative to platform threads for I/O-bound workloads. + +Until now, platform threads were the concurrency unit of the JVM. +They are a wrapper over OS structures. +This means that creating a Java platform thread actually results in creating a "thread-like" structure in your operating system. + +Virtual threads on the other hand are managed by the JVM. In order to be executed, they need to be mounted on a platform thread +(which acts as a carrier to that virtual thread). +As such, they have been designed to offer the following characteristics: + +Lightweight :: Virtual threads occupy less space than platform threads in memory. +Hence, it becomes possible to use more virtual threads than platform threads simultaneously without blowing up the heap. +By default, platform threads are created with a stack of about 1 MB where virtual threads stack is "pay-as-you-go". +You can find these numbers along with other motivations for virtual threads in this presentation given by the lead developer of project Loom: https://youtu.be/lIq-x_iI-kc?t=543. + +Cheap to create:: Creating a platform thread in Java takes time. +Currently, techniques such as pooling where threads are created once then reused are strongly encouraged to minimize the +time lost in starting them (as well as limiting the maximum number of threads to keep memory consumption low). +Virtual threads are supposed to be disposable entities that we create when we need them, +it is discouraged to pool them or to reuse them for different tasks. + +Cheap to block:: When performing blocking I/O, the underlying OS thread wrapped by the Java platform thread is put in a +wait queue and a context switch occurs to load a new thread context onto the CPU core. This operation takes time. +Since virtual threads are managed by the JVM, no underlying OS thread is blocked when they perform a blocking operation. +Their state is simply stored in the heap and another Virtual thread is executed on the same Java platform thread. + +=== Virtual threads are useful for I/O-bound workloads only +We now know that we can create way more virtual threads than platform threads. One could be tempted to use virtual threads +to perform long computations (CPU-bound workload). +This is useless if not counterproductive. +CPU-bound doesn't consist in quickly swapping threads while they need to wait for the completion of an I/O but in leaving +them attached to a CPU-core to actually compute something. +In this scenario, it is useless to have thousands of threads if we have tens of CPU-cores, virtual threads won't enhance +the performance of CPU-bound workloads. + + +== Bringing virtual threads to reactive REST services +Since virtual threads are disposable entities, the fundamental idea of quarkus-loom is to offload the execution of an +endpoint handler on a new virtual thread instead of running it on an event-loop (in the case of RESTeasy-reactive) or a +platform worker thread. + +To do so, it suffices to add the link:{runonvthread}[@RunOnVirtualThread] annotation to the endpoint. +If the JDK is compatible (Java 19 or later versions) then the endpoint will be offloaded to a virtual thread. +It will then be possible to perform blocking operations without blocking the platform thread upon which the virtual +thread is mounted. + +This annotation can only be used in conjunction with endpoints annotated with link:{blockingannotation}[@Blocking] or +considered blocking because of their signature. +You can visit xref:resteasy-reactive.adoc#execution-model-blocking-non-blocking[Execution model, blocking, non-blocking] +for more information. + +=== Getting started + +Add the following import to your build file: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-resteasy-reactive + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-resteasy-reactive") +---- + +You also need to make sure that you are using the version 19 of Java, this can be enforced in your pom.xml file with the following: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + 19 + 19 + +---- + +Virtual threads are still an experimental feature, you need to start your application with the `--enable-preview` flag: + +[source, bash] +---- +java --enable-preview -jar target/quarkus-app/quarkus-run.jar +---- + +The example below shows the differences between three endpoints, all of them querying a fortune in the database then +returning it to the client. + +- the first one uses the traditional blocking style, it is considered blocking due to its signature. +- the second one uses Mutiny reactive streams in a declarative style, it is considered non-blocking due to its signature. +- the third one uses Mutiny reactive streams in a synchronous way, since it doesn't return a "reactive type" it is +considered blocking and the link:{runonvthread}[@RunOnVirtualThread] annotation can be used. + +When using Mutiny, alternative "xAndAwait" methods are provided to be used with virtual threads. +They ensure that waiting for the completion of the I/O will not "pin" the carrier thread and deteriorate performance. +Pinning is a phenomenon that we describe in xref:Pinning cases[this section]. + + +In other words, the mutiny environment is a safe environment for virtual threads. +The guarantees offered by Mutiny are detailed later. + +[source,java] +---- +package org.acme.rest; + +import org.acme.fortune.model.Fortune; +import org.acme.fortune.repository.FortuneRepository; +import io.smallrye.common.annotation.RunOnVirtualThread; +import io.smallrye.mutiny.Uni; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.List; +import java.util.Random; + + +@Path("") +public class FortuneResource { + + @GET + @Path("/blocking") + public Fortune blocking() { + var list = repository.findAllBlocking(); + return pickOne(list); + } + + @GET + @Path("/reactive") + public Uni reactive() { + return repository.findAllAsync() + .map(this::pickOne); + } + + @GET + @Path("/virtual") + @RunOnVirtualThread + public Fortune virtualThread() { + var list = repository.findAllAsyncAndAwait(); + return pickOne(list); + } + +} +---- + +=== Simplifying complex logic +The previous example is trivial and doesn't capture how imperative style can simplify complex reactive operations. +Below is a more complex example. +The endpoints must now fetch all the fortunes in the database, then append a quote to each fortune before finally returning +the result to the client. + + + +[source,java] +---- +package org.acme.rest; + +import org.acme.fortune.model.Fortune; +import org.acme.fortune.repository.FortuneRepository; +import io.smallrye.common.annotation.RunOnVirtualThread; +import io.smallrye.mutiny.Uni; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.List; +import java.util.Random; + + +@Path("") +public class FortuneResource { + + private final FortuneRepository repository; + + public Uni> getQuotesAsync(int size){ + //... + //asynchronously returns a list of quotes from an arbitrary source + } + + @GET + @Path("/quoted-blocking") + public List getAllQuotedBlocking() { + // we get the list of fortunes + var fortunes = repository.findAllBlocking(); + + // we get the list of quotes + var quotes = getQuotes(fortunes.size()).await().indefinitely(); + + // we append each quote to each fortune + for(int i=0; i < fortunes.size();i ++){ + fortunes.get(i).title+= " - "+quotes.get(i); + } + return todos; + } + + @GET + @Path("/quoted-reactive") + public Uni> getAllQuoted() { + // we first fetch the list of resource and we memoize it + // to avoid fetching it again everytime need it + var fortunes = repository.findAllAsync().memoize().indefinitely(); + + // once we get a result for fortunes, + // we know its size and can thus query the right number of quotes + var quotes = fortunes.onItem().transformToUni(list -> getQuotes(list.size())); + + // we now need to combine the two reactive streams + // before returning the result to the user + return Uni.combine().all().unis(fortunes,quotes).asTuple().onItem().transform(tuple -> { + var todoList=tuple.getItem1(); + //can await it since it is already resolved + var quotesList = tuple.getItem2(); + for(int i=0; i < todoList.size();i ++){ + todoList.get(i).title+= " - "+quotesList.get(i); + } + return todoList; + }); + } + + @GET + @RunOnVirtualThread + @Path("/quoted-virtual-thread") + public List getAllQuotedBlocking() { + //we get the list of fortunes + var fortunes = repository.findAllAsyncAndAwait(); + + //we get the list of quotes + var quotes = getQuotes(fortunes.size()).await().indefinitely(); + + //we append each quote to each fortune + for(int i=0; i < fortunes.size();i ++){ + fortunes.get(i).title+= " - "+quotes.get(i); + } + return todos; + } + +} +---- + +== Pinning cases +The notion of "cheap blocking" might not always be true: in certain occasions a virtual thread might "pin" its carrier +(the platform thread it is mounted upon). +In this situation, the platform thread is blocked exactly as it would have been in a typical blocking scenario. + +According to link:{vthreadjep}[JEP 425] this can happen in two situations: + +- when a virtual thread executes performs a blocking operation inside a `synchronized` block or method +- when it executes a blocking operation inside a native method or a foreign function + +It can be fairly easy to avoid these situations in our own code, but it is hard to verify every dependency we use. +Typically, while experimenting with virtual-threads, we realized that using the link:{pgsql-driver}[postgresql-JDBC driver] +results in frequent pinning. + +=== The JDBC problem +Our experiments so far show that when a virtual thread queries a database using the JDBC driver, it will pin its carrier +thread during the entire operation. + +Let's show the code of the `findAllBlocking()` method we used in the first example + +[source, java] +---- +//import ... + +@ApplicationScoped +public class FortuneRepository { + // ... + + public List findAllBlocking() { + List fortunes = new ArrayList<>(); + Connection conn = null; + try { + conn = db.getJdbcConnection(); + var preparedStatement = conn.prepareStatement(SELECT_ALL); + ResultSet rs = preparedStatement.executeQuery(); + while (rs.next()) { + fortunes.add(create(rs)); + } + rs.close(); + preparedStatement.close(); + } catch (SQLException e) { + logger.warn("Unable to retrieve fortunes from the database", e); + } finally { + close(conn); + } + return fortunes; + } + + //... +} +---- + +The actual query happens at `ResultSet rs = preparedStatement.executeQuery();`, here is how it is implemented in the +postgresql-jdbc driver 42.5.0: + +[source, java] +---- +class PgPreparedStatement extends PgStatement implements PreparedStatement { + // ... + + /* + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the * query - never null + * + * @exception SQLException if a database access error occurs + */ + @Override + public ResultSet executeQuery() throws SQLException { + synchronized (this) { + if (!executeWithFlags(0)) { + throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA); + } + return getSingleResultSet(); + } + } + + // ... +} +---- + +This `synchronized` block is the culprit. +Replacing it with a lock is a good solution, but it won't be enough: `synchronized` blocks are also used in `executeWithFlags(int flag)`. +A systematic review of the postgresql-jdbc driver is necessary to make sure that it is compliant with virtual threads. + +=== Reactive drivers at the rescue +The vertx-sql-client is a reactive client, hence it is not supposed to block while waiting for the completion of a +transaction with the database. +However, when using the link:{mutiny-vertx-sql}[smallrye-mutiny-vertx-sqlclient] it is possible to use a variant method +that will await for the completion of the transaction, mimicking a blocking behaviour. + +Below is the `FortuneRepository` except the blocking we've seen earlier has been replaced by reactive methods. + +[source, java] +---- +//import ... + +@ApplicationScoped +public class FortuneRepository { + // ... + + public Uni> findAllAsync() { + return db.getPool() + .preparedQuery(SELECT_ALL).execute() + .map(this::createListOfFortunes); + + } + + public List findAllAsyncAndAwait() { + var rows = db.getPool().preparedQuery(SELECT_ALL) + .executeAndAwait(); + return createListOfFortunes(rows); + } + + //... +} +---- + +Contrary to the link:{pgsql-driver}[postgresql-jdbc driver], no `synchronized` block is used where it shouldn't be, and +the `await` behaviour is implemented using locks and latches that won't cause pinning. + +Using the synchronous methods of the link:{mutiny-vertx-sql}[smallrye-mutiny-vertx-sqlclient] along with virtual threads +will allow you to use the synchronous blocking style, avoid pinning the carrier thread, and get performance close to a pure +reactive implementation. + +== A point about performance + +Our experiments seem to indicate that Quarkus with virtual threads will scale better than Quarkus blocking (offloading +the computation on a pool of platform worker threads) but not as well Quarkus reactive. +The memory consumption especially might be an issue: if your system needs to keep its memory footprint low we would +advise you stick to using reactive constructs. + +This degradation of performance doesn't seem to come from virtual threads themselves but from the interactions between +Vert.x/Netty (Quarkus underlying reactive engine) and the virtual threads. +This was illustrated in the issue that we will now describe. + +=== The Netty problem +For JSON serialization, Netty uses their custom implementation of thread locals, `FastThreadLocal` to store buffers. +When using virtual threads in quarkus, then number of virtual threads simultaneously living in the service is directly +related to the incoming traffic. +It is possible to get hundreds of thousands, if not millions, of them. + +If they need to serialize some data to JSON they will end up creating as many instances of `FastThreadLocal`, resulting +on a massive memory consumption as well as exacerbated pressure on the garbage collector. +This will eventually affect the performance of the application and inhibit its scalability. + +This is a perfect example of the mismatch between the reactive stack and the virtual threads. +The fundamental hypothesis are completely different and result in different optimizations. +Netty expects a system using few event-loops (as many event-loops as CPU cores by default in Quarkus), but it gets hundreds +of thousands of threads. +You can refer to link:https://mail.openjdk.org/pipermail/loom-dev/2022-July/004844.html[this mail] to get more information +on how we envision our future with virtual threads. + +=== Our solution to the Netty problem +In order to avoid this wasting of resource without modifying Netty upstream, we wrote an extension that modifies the +bytecode of the class responsible for creating the thread locals at build time. +Using this extension, performance of virtual threads in Quarkus for the Json Serialization test of the Techempower suite +increased by nearly 80%, making it almost as good as reactive endpoints. + +To use it, it needs to be added as a dependency: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-netty-loom-adaptor + +---- + +Furthermore, some operations undertaken by this extension need special access, it is necessary to + +- compile the application with the flag `-Dnet.bytebuddy.experimental` +- open the `java.base.lang` module at runtime with the flag `--add-opens java.base/java.lang=ALL-UNNAMED` + +This extension is only intended to improve performance, it is perfectly fine not to use it. + +=== Concerning dev mode +If you want to use quarkus with the dev mode, it won't be possible to manually specify the flags we mentioned along this guide. +Instead, you want to specify them all in the configuration of the `quarkus-maven-plugin` as presented below. + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + build + + + + + + 19 + 19 + + --enable-preview + -Dnet.bytebuddy.experimental + + --enable-preview --add-opens java.base/java.lang=ALL-UNNAMED + + + +---- + +If you don't want to put specify the opening the `java.lang` module in your pom.xml file, you can also specify it as an argument +when you start the dev mode. + +The configuration of the quarkus-maven-plugin will be simpler: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + 19 + 19 + + --enable-preview + -Dnet.bytebuddy.experimental + + --enable-preview + +---- + +And the command will become: + +[source, bash] +---- +mvn quarkus:dev -Dopen-lang-package +---- diff --git a/docs/src/main/java/io/quarkus/docs/generation/QuarkusMavenPluginDocsGenerator.java b/docs/src/main/java/io/quarkus/docs/generation/QuarkusMavenPluginDocsGenerator.java new file mode 100644 index 0000000000000..0020be0a5c902 --- /dev/null +++ b/docs/src/main/java/io/quarkus/docs/generation/QuarkusMavenPluginDocsGenerator.java @@ -0,0 +1,106 @@ +package io.quarkus.docs.generation; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.codehaus.plexus.util.xml.XmlStreamReader; + +import io.quarkus.annotation.processor.Constants; +import io.quarkus.annotation.processor.generate_doc.ConfigDocWriter; +import io.quarkus.annotation.processor.generate_doc.MavenConfigDocBuilder; +import io.quarkus.annotation.processor.generate_doc.MavenConfigDocBuilder.GoalParamsBuilder; + +/** + * Generates documentation for the Quarkus Maven Plugin from plugin descriptor. + */ +public class QuarkusMavenPluginDocsGenerator { + + private static final String QUARKUS_MAVEN_PLUGIN = "quarkus-maven-plugin-"; + private static final String GOALS_OUTPUT_FILE_NAME = QUARKUS_MAVEN_PLUGIN + "goals" + Constants.ADOC_EXTENSION; + private static final String GOAL_PARAMETER_ANCHOR_PREFIX = QUARKUS_MAVEN_PLUGIN + "goal-%s-"; + + public static void main(String[] args) throws Exception { + + String errorMessage = null; + + // Path to Quarkus Maven Plugin descriptor (plugin.xml) + final Path pluginXmlDescriptorPath; + if (args.length == 1) { + pluginXmlDescriptorPath = Path.of(args[0]); + } else { + pluginXmlDescriptorPath = null; + errorMessage = String.format("Expected 1 argument ('plugin.xml' file path), got %s", args.length); + } + + // Check the file exist + if (pluginXmlDescriptorPath != null + && (!Files.exists(pluginXmlDescriptorPath) || !Files.isRegularFile(pluginXmlDescriptorPath))) { + errorMessage = String.format("File does not exist: %s", pluginXmlDescriptorPath.toAbsolutePath()); + } + + // Deserialize plugin.xml to PluginDescriptor + PluginDescriptor pluginDescriptor = null; + if (errorMessage == null) { + try (Reader input = new XmlStreamReader(new FileInputStream(pluginXmlDescriptorPath.toFile()))) { + pluginDescriptor = new PluginDescriptorBuilder().build(input); + } catch (IOException e) { + errorMessage = String.format("Failed to deserialize PluginDescriptor: %s", e.getMessage()); + } + } + + // Don't generate documentation if there are no goals (shouldn't happen if correct descriptor is available) + if (pluginDescriptor != null && (pluginDescriptor.getMojos() == null || pluginDescriptor.getMojos().isEmpty())) { + errorMessage = "Found no goals"; + } + + // Don't break the build if Quarkus Maven Plugin Descriptor is not available + if (errorMessage != null) { + System.err.printf("Can't generate the documentation for the Quarkus Maven Plugin\n: %s\n", errorMessage); + return; + } + + // Build Goals documentation + final var goalsConfigDocBuilder = new MavenConfigDocBuilder(); + for (MojoDescriptor mojo : pluginDescriptor.getMojos()) { + + // Add Goal Title + goalsConfigDocBuilder.addTableTitle(mojo.getFullGoalName()); + + // Add Goal Description + if (mojo.getDescription() != null && !mojo.getDescription().isBlank()) { + goalsConfigDocBuilder.addTableDescription(mojo.getDescription()); + } + + // Collect Goal Parameters + final GoalParamsBuilder goalParamsBuilder = goalsConfigDocBuilder.newGoalParamsBuilder(); + if (mojo.getParameters() != null) { + for (Parameter parameter : mojo.getParameters()) { + goalParamsBuilder.addParam(parameter.getType(), parameter.getName(), parameter.getDefaultValue(), + parameter.isRequired(), parameter.getDescription()); + } + } + + // Add Parameters Summary Table if the goal has parameters + if (goalParamsBuilder.tableIsNotEmpty()) { + goalsConfigDocBuilder.addSummaryTable(String.format(GOAL_PARAMETER_ANCHOR_PREFIX, mojo.getGoal()), false, + goalParamsBuilder.build(), GOALS_OUTPUT_FILE_NAME, false); + + // Start next table on a new line + goalsConfigDocBuilder.addNewLine(); + } + } + + // Generate Goals documentation + if (goalsConfigDocBuilder.hasWriteItems()) { + new ConfigDocWriter().generateDocumentation(GOALS_OUTPUT_FILE_NAME, goalsConfigDocBuilder); + } + } + +} diff --git a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java index e90502dc4dfe2..769ad707fd29e 100644 --- a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java +++ b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java @@ -40,7 +40,6 @@ import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; @@ -49,7 +48,6 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.narayana.jta.deployment.NarayanaInitBuildItem; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; @@ -223,7 +221,6 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, @Record(ExecutionTime.RUNTIME_INIT) @BuildStep - @Consume(NarayanaInitBuildItem.class) void generateDataSourceBeans(AgroalRecorder recorder, DataSourcesRuntimeConfig dataSourcesRuntimeConfig, List aggregatedBuildTimeConfigBuildItems, diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java index 9adbe0abf066c..5008a18b6da99 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java @@ -22,7 +22,6 @@ import javax.transaction.TransactionSynchronizationRegistry; import org.jboss.logging.Logger; -import org.jboss.tm.XAResourceRecoveryRegistry; import io.agroal.api.AgroalDataSource; import io.agroal.api.AgroalPoolInterceptor; @@ -73,7 +72,6 @@ public class DataSources { private final DataSourcesJdbcBuildTimeConfig dataSourcesJdbcBuildTimeConfig; private final DataSourcesJdbcRuntimeConfig dataSourcesJdbcRuntimeConfig; private final TransactionManager transactionManager; - private final XAResourceRecoveryRegistry xaResourceRecoveryRegistry; private final TransactionSynchronizationRegistry transactionSynchronizationRegistry; private final DataSourceSupport dataSourceSupport; private final Instance agroalPoolInterceptors; @@ -84,7 +82,6 @@ public DataSources(DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, DataSourcesRuntimeConfig dataSourcesRuntimeConfig, DataSourcesJdbcBuildTimeConfig dataSourcesJdbcBuildTimeConfig, DataSourcesJdbcRuntimeConfig dataSourcesJdbcRuntimeConfig, TransactionManager transactionManager, - XAResourceRecoveryRegistry xaResourceRecoveryRegistry, TransactionSynchronizationRegistry transactionSynchronizationRegistry, DataSourceSupport dataSourceSupport, @Any Instance agroalPoolInterceptors) { this.dataSourcesBuildTimeConfig = dataSourcesBuildTimeConfig; @@ -92,7 +89,6 @@ public DataSources(DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, this.dataSourcesJdbcBuildTimeConfig = dataSourcesJdbcBuildTimeConfig; this.dataSourcesJdbcRuntimeConfig = dataSourcesJdbcRuntimeConfig; this.transactionManager = transactionManager; - this.xaResourceRecoveryRegistry = xaResourceRecoveryRegistry; this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; this.dataSourceSupport = dataSourceSupport; this.agroalPoolInterceptors = agroalPoolInterceptors; @@ -272,10 +268,7 @@ private void applyNewConfiguration(AgroalDataSourceConfigurationSupplier dataSou if (dataSourceJdbcBuildTimeConfig.transactions != io.quarkus.agroal.runtime.TransactionIntegration.DISABLED) { TransactionIntegration txIntegration = new NarayanaTransactionIntegration(transactionManager, - transactionSynchronizationRegistry, null, false, - dataSourceJdbcBuildTimeConfig.transactions == io.quarkus.agroal.runtime.TransactionIntegration.XA - ? xaResourceRecoveryRegistry - : null); + transactionSynchronizationRegistry); poolConfiguration.transactionIntegration(txIntegration); } @@ -294,14 +287,12 @@ private void applyNewConfiguration(AgroalDataSourceConfigurationSupplier dataSou // Authentication if (dataSourceRuntimeConfig.username.isPresent()) { - NamePrincipal username = new NamePrincipal(dataSourceRuntimeConfig.username.get()); connectionFactoryConfiguration - .principal(username).recoveryPrincipal(username); + .principal(new NamePrincipal(dataSourceRuntimeConfig.username.get())); } if (dataSourceRuntimeConfig.password.isPresent()) { - SimplePassword password = new SimplePassword(dataSourceRuntimeConfig.password.get()); connectionFactoryConfiguration - .credential(password).recoveryCredential(password); + .credential(new SimplePassword(dataSourceRuntimeConfig.password.get())); } // credentials provider diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LoggingBeanSupportProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LoggingBeanSupportProcessor.java new file mode 100644 index 0000000000000..71bdfdb1b8160 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LoggingBeanSupportProcessor.java @@ -0,0 +1,16 @@ +package io.quarkus.arc.deployment; + +import io.quarkus.arc.processor.BuiltinScope; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.logging.LoggingResourceProcessor; + +public class LoggingBeanSupportProcessor { + + @BuildStep + public void discoveredComponents(BuildProducer beanDefiningAnnotationProducer) { + beanDefiningAnnotationProducer.produce(new BeanDefiningAnnotationBuildItem( + LoggingResourceProcessor.LOGGING_FILTER, BuiltinScope.SINGLETON.getName(), false)); + } + +} diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/logging/ArcLogFilterFactory.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/logging/ArcLogFilterFactory.java new file mode 100644 index 0000000000000..f653c300c3ada --- /dev/null +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/logging/ArcLogFilterFactory.java @@ -0,0 +1,29 @@ +package io.quarkus.arc.runtime.logging; + +import java.util.logging.Filter; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.runtime.logging.LogFilterFactory; + +/** + * Creates the implementation of the class by getting a bean from Arc. + * This class is loaded automatically by the {@link java.util.ServiceLoader}. + */ +public class ArcLogFilterFactory implements LogFilterFactory { + + @Override + public Filter create(String className) throws Exception { + InstanceHandle instance = Arc.container().instance(Class.forName(className, true, Thread.currentThread() + .getContextClassLoader())); + if (!instance.isAvailable()) { + throw new IllegalStateException("Improper integration of '" + LogFilterFactory.class.getName() + "' detected"); + } + return (Filter) instance.get(); + } + + @Override + public int priority() { + return LogFilterFactory.DEFAULT_PRIORITY - 100; + } +} diff --git a/extensions/arc/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.logging.LogFilterFactory b/extensions/arc/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.logging.LogFilterFactory new file mode 100644 index 0000000000000..d114e9f1e84d4 --- /dev/null +++ b/extensions/arc/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.logging.LogFilterFactory @@ -0,0 +1 @@ +io.quarkus.arc.runtime.logging.ArcLogFilterFactory diff --git a/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml index d307ac236dbbd..eee08b1544b0d 100644 --- a/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -9,7 +9,7 @@ metadata: guide: "https://quarkus.io/guides/funqy-knative-events" categories: - "cloud" - status: "experimental" + status: "stable" codestart: name: "funqy-knative-events" kind: "example" diff --git a/extensions/hibernate-orm/runtime/pom.xml b/extensions/hibernate-orm/runtime/pom.xml index 425c4401f8976..acacdcf4e5c77 100644 --- a/extensions/hibernate-orm/runtime/pom.xml +++ b/extensions/hibernate-orm/runtime/pom.xml @@ -91,7 +91,7 @@ - org.jboss + io.smallrye jandex diff --git a/extensions/hibernate-search-orm-coordination-outbox-polling/deployment/src/test/java/io/quarkus/hibernate/search/orm/coordination/outboxpolling/test/HibernateSearchOutboxPollingClassesTest.java b/extensions/hibernate-search-orm-coordination-outbox-polling/deployment/src/test/java/io/quarkus/hibernate/search/orm/coordination/outboxpolling/test/HibernateSearchOutboxPollingClassesTest.java index 15c55c8c03e26..be49d54a33287 100644 --- a/extensions/hibernate-search-orm-coordination-outbox-polling/deployment/src/test/java/io/quarkus/hibernate/search/orm/coordination/outboxpolling/test/HibernateSearchOutboxPollingClassesTest.java +++ b/extensions/hibernate-search-orm-coordination-outbox-polling/deployment/src/test/java/io/quarkus/hibernate/search/orm/coordination/outboxpolling/test/HibernateSearchOutboxPollingClassesTest.java @@ -165,6 +165,7 @@ private static void collectModelClassesRecursively(Index index, Type type, Setassertj-core test
+ + org.eclipse.transformer + org.eclipse.transformer + 0.5.0 + diff --git a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JakartaEnablement.java b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JakartaEnablement.java new file mode 100644 index 0000000000000..c74ed6b81b965 --- /dev/null +++ b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JakartaEnablement.java @@ -0,0 +1,82 @@ +package io.quarkus.jdbc.db2.deployment; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.transformer.action.ActionContext; +import org.eclipse.transformer.action.ByteData; +import org.eclipse.transformer.action.impl.ActionContextImpl; +import org.eclipse.transformer.action.impl.ByteDataImpl; +import org.eclipse.transformer.action.impl.ClassActionImpl; +import org.eclipse.transformer.action.impl.SelectionRuleImpl; +import org.eclipse.transformer.action.impl.SignatureRuleImpl; +import org.eclipse.transformer.util.FileUtils; +import org.objectweb.asm.ClassReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; + +/** + * The DB2 driver is compiled using references to classes in the javax.transaction + * package; we need to transform these to fix compatibility with jakarta.transaction. + * We do this by leveraging the Eclipse Transformer project during Augmentation, so + * that end users don't need to bother. + */ +public class JakartaEnablement { + + private static final List CLASSES_NEEDING_TRANSFORMATION = List.of( + "com.ibm.db2.jcc.t2zos.ab", + "com.ibm.db2.jcc.t2zos.T2zosConnection", + "com.ibm.db2.jcc.t2zos.T2zosConfiguration"); + + @BuildStep + void transformToJakarta(BuildProducer transformers) { + if (QuarkusClassLoader.isClassPresentAtRuntime("jakarta.transaction.Transaction")) { + JakartaTransformer tr = new JakartaTransformer(); + for (String classname : CLASSES_NEEDING_TRANSFORMATION) { + final BytecodeTransformerBuildItem item = new BytecodeTransformerBuildItem.Builder() + .setCacheable(true) + .setContinueOnFailure(false) + .setClassToTransform(classname) + .setClassReaderOptions(ClassReader.SKIP_DEBUG) + .setInputTransformer(tr::transform) + .build(); + transformers.produce(item); + } + } + } + + private static class JakartaTransformer { + + private final Logger logger; + private final ActionContext ctx; + + JakartaTransformer() { + logger = LoggerFactory.getLogger("JakartaTransformer"); + Map renames = new HashMap<>(); + //N.B. we enable only this transformation, not the full set of capabilities of Eclipse Transformer; + //this might need tailoring if the same idea gets applied to a different context. + renames.put("javax.transaction", "jakarta.transaction"); + ctx = new ActionContextImpl(logger, + new SelectionRuleImpl(logger, Collections.emptyMap(), Collections.emptyMap()), + new SignatureRuleImpl(logger, renames, null, null, null, null, null, Collections.emptyMap())); + } + + byte[] transform(final String name, final byte[] bytes) { + logger.info("Jakarta EE compatibility enhancer for Quarkus: transforming " + name); + final ClassActionImpl classTransformer = new ClassActionImpl(ctx); + final ByteBuffer input = ByteBuffer.wrap(bytes); + final ByteData inputData = new ByteDataImpl(name, input, FileUtils.DEFAULT_CHARSET); + final ByteData outputData = classTransformer.apply(inputData); + return outputData.buffer().array(); + } + } + +} diff --git a/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaInitBuildItem.java b/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaInitBuildItem.java deleted file mode 100644 index 5cbee9c7ce40d..0000000000000 --- a/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaInitBuildItem.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.quarkus.narayana.jta.deployment; - -import io.quarkus.builder.item.EmptyBuildItem; - -/** - * Marker build item that indicates that the Narayana JTA extension has been initialized. - */ -public final class NarayanaInitBuildItem extends EmptyBuildItem { -} diff --git a/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaJtaProcessor.java b/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaJtaProcessor.java index 18e88ec312178..397f771bdfc79 100644 --- a/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaJtaProcessor.java +++ b/extensions/narayana-jta/deployment/src/main/java/io/quarkus/narayana/jta/deployment/NarayanaJtaProcessor.java @@ -10,20 +10,11 @@ import javax.transaction.TransactionScoped; import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean; -import com.arjuna.ats.arjuna.recovery.TransactionStatusConnectionManager; import com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple; import com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore; -import com.arjuna.ats.internal.arjuna.recovery.AtomicActionExpiryScanner; -import com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule; -import com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner; import com.arjuna.ats.internal.arjuna.utils.SocketProcessId; import com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule; -import com.arjuna.ats.internal.jta.recovery.arjunacore.JTAActionStatusServiceXAResourceOrphanFilter; -import com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter; -import com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter; import com.arjuna.ats.internal.jta.recovery.arjunacore.RecoverConnectableAtomicAction; -import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule; -import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord; import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple; import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple; import com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple; @@ -41,7 +32,6 @@ import io.quarkus.deployment.IsTest; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.Produce; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; @@ -74,7 +64,6 @@ public NativeImageSystemPropertyBuildItem nativeImageSystemPropertyBuildItem() { @BuildStep @Record(RUNTIME_INIT) - @Produce(NarayanaInitBuildItem.class) public void build(NarayanaJtaRecorder recorder, BuildProducer additionalBeans, BuildProducer reflectiveClass, @@ -92,9 +81,6 @@ public void build(NarayanaJtaRecorder recorder, runtimeInit.produce(new RuntimeInitializedClassBuildItem(SocketProcessId.class.getName())); runtimeInit.produce(new RuntimeInitializedClassBuildItem(CommitMarkableResourceRecordRecoveryModule.class.getName())); runtimeInit.produce(new RuntimeInitializedClassBuildItem(RecoverConnectableAtomicAction.class.getName())); - runtimeInit.produce(new RuntimeInitializedClassBuildItem(TransactionStatusConnectionManager.class.getName())); - runtimeInit.produce(new RuntimeInitializedClassBuildItem(JTAActionStatusServiceXAResourceOrphanFilter.class.getName())); - runtimeInit.produce(new RuntimeInitializedClassBuildItem(AtomicActionExpiryScanner.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, JTAEnvironmentBean.class.getName(), UserTransactionImple.class.getName(), @@ -103,14 +89,7 @@ public void build(NarayanaJtaRecorder recorder, TransactionSynchronizationRegistryImple.class.getName(), ObjectStoreEnvironmentBean.class.getName(), ShadowNoFileLockStore.class.getName(), - SocketProcessId.class.getName(), - AtomicActionRecoveryModule.class.getName(), - XARecoveryModule.class.getName(), - XAResourceRecord.class.getName(), - JTATransactionLogXAResourceOrphanFilter.class.getName(), - JTANodeNameXAResourceOrphanFilter.class.getName(), - JTAActionStatusServiceXAResourceOrphanFilter.class.getName(), - ExpiredTransactionStatusManagerScanner.class.getName())); + SocketProcessId.class.getName())); AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder(); builder.addBeanClass(TransactionalInterceptorSupports.class); diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaProducers.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaProducers.java index abfb7d0d36f97..85e4b99d2e17e 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaProducers.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaProducers.java @@ -3,7 +3,6 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Dependent; import javax.enterprise.inject.Produces; -import javax.inject.Singleton; import javax.transaction.TransactionSynchronizationRegistry; import org.jboss.tm.JBossXATerminator; @@ -33,12 +32,9 @@ public javax.transaction.UserTransaction userTransaction() { } @Produces - @Singleton + @ApplicationScoped public XAResourceRecoveryRegistry xaResourceRecoveryRegistry() { - RecoveryManagerService recoveryManagerService = new RecoveryManagerService(); - recoveryManagerService.create(); - recoveryManagerService.start(); - return recoveryManagerService; + return new RecoveryManagerService(); } @Produces diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java index 82d2e61b95ec4..b66dd67487d37 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java @@ -7,15 +7,10 @@ import org.jboss.logging.Logger; import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException; -import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean; -import com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean; import com.arjuna.ats.arjuna.common.arjPropertyManager; import com.arjuna.ats.arjuna.coordinator.TransactionReaper; import com.arjuna.ats.arjuna.coordinator.TxControl; -import com.arjuna.ats.arjuna.recovery.RecoveryManager; -import com.arjuna.ats.jta.common.JTAEnvironmentBean; import com.arjuna.ats.jta.common.jtaPropertyManager; -import com.arjuna.common.internal.util.propertyservice.BeanPopulator; import com.arjuna.common.util.propertyservice.PropertiesFactory; import io.quarkus.runtime.ShutdownContext; @@ -72,25 +67,13 @@ public void disableTransactionStatusManager() { } public void setConfig(final TransactionManagerConfiguration transactions) { - BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class) - .setObjectStoreDir(transactions.objectStoreDirectory); - BeanPopulator.getNamedInstance(ObjectStoreEnvironmentBean.class, "communicationStore") - .setObjectStoreDir(transactions.objectStoreDirectory); - BeanPopulator.getNamedInstance(ObjectStoreEnvironmentBean.class, "stateStore") - .setObjectStoreDir(transactions.objectStoreDirectory); - BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class) - .setRecoveryModuleClassNames(transactions.recoveryModules); - BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class) - .setExpiryScannerClassNames(transactions.expiryScanners); - BeanPopulator.getDefaultInstance(JTAEnvironmentBean.class) - .setXaResourceOrphanFilterClassNames(transactions.xaResourceOrphanFilters); + arjPropertyManager.getObjectStoreEnvironmentBean().setObjectStoreDir(transactions.objectStoreDirectory); } public void handleShutdown(ShutdownContext context) { context.addLastShutdownTask(new Runnable() { @Override public void run() { - RecoveryManager.manager().terminate(true); TransactionReaper.terminate(false); } }); diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java index b4f8ab1ee8cc1..ee2c70841b308 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java @@ -1,7 +1,6 @@ package io.quarkus.narayana.jta.runtime; import java.time.Duration; -import java.util.List; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; @@ -31,25 +30,4 @@ public final class TransactionManagerConfiguration { */ @ConfigItem(defaultValue = "ObjectStore") public String objectStoreDirectory; - - /** - * The list of recovery modules - */ - @ConfigItem(defaultValue = "com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule," + - "com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule") - public List recoveryModules; - - /** - * The list of expiry scanners - */ - @ConfigItem(defaultValue = "com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner") - public List expiryScanners; - - /** - * The list of orphan filters - */ - @ConfigItem(defaultValue = "com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter," + - "com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter," + - "com.arjuna.ats.internal.jta.recovery.arjunacore.JTAActionStatusServiceXAResourceOrphanFilter") - public List xaResourceOrphanFilters; } diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java index 27590f08142b4..c6a052ad3b0cd 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java @@ -27,7 +27,7 @@ public class DevServicesConfig { * * Image with a Quarkus based distribution is used by default. * Image with a WildFly based distribution can be selected instead, for example: - * 'quay.io/keycloak/keycloak:19.0.1-legacy'. + * 'quay.io/keycloak/keycloak:19.0.2-legacy'. *

* Note Keycloak Quarkus and Keycloak WildFly images are initialized differently. * By default, Dev Services for Keycloak will assume it is a Keycloak Quarkus image if the image version does not end with a @@ -35,7 +35,7 @@ public class DevServicesConfig { * string. * Set 'quarkus.keycloak.devservices.keycloak-x-image' to override this check. */ - @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:19.0.1") + @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:19.0.2") public String imageName; /** diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index 2421c3b3b6dd3..7fdfea032c413 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -250,7 +250,7 @@ public Uni apply(Throwable t) { } if (!configContext.oidcConfig.token.refreshExpired) { LOG.debug("Token has expired, token refresh is not allowed"); - throw new AuthenticationCompletionException(t.getCause()); + throw new AuthenticationFailedException(t.getCause()); } LOG.debug("Token has expired, trying to refresh it"); return refreshSecurityIdentity(configContext, @@ -833,7 +833,7 @@ private Uni refreshSecurityIdentity(TenantConfigContext config @Override public Uni apply(final AuthorizationCodeTokens tokens, final Throwable t) { if (t != null) { - LOG.debugf("ID token refresh has failed: %s", t.getMessage()); + LOG.errorf("ID token refresh has failed: %s", t.getMessage()); if (autoRefresh) { LOG.debug("Using the current SecurityIdentity since the ID token is still valid"); return Uni.createFrom().item(((TokenAutoRefreshException) t).getSecurityIdentity()); diff --git a/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml b/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml index 66f4191d3f9b9..3b30214d930ac 100644 --- a/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml +++ b/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml @@ -135,7 +135,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/hibernate-orm-panache/runtime/pom.xml b/extensions/panache/hibernate-orm-panache/runtime/pom.xml index 5eae026f64935..79101f6144cc6 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/pom.xml +++ b/extensions/panache/hibernate-orm-panache/runtime/pom.xml @@ -96,7 +96,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/hibernate-reactive-panache/runtime/pom.xml b/extensions/panache/hibernate-reactive-panache/runtime/pom.xml index 8f177204ea95c..f3021f1329f25 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/pom.xml +++ b/extensions/panache/hibernate-reactive-panache/runtime/pom.xml @@ -104,7 +104,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/mongodb-panache-common/runtime/pom.xml b/extensions/panache/mongodb-panache-common/runtime/pom.xml index c6decdfdad1ab..a0ee38dc6a428 100644 --- a/extensions/panache/mongodb-panache-common/runtime/pom.xml +++ b/extensions/panache/mongodb-panache-common/runtime/pom.xml @@ -73,7 +73,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml b/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml index 962592a648616..ef6c31f23d2f4 100644 --- a/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml +++ b/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml @@ -96,7 +96,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/mongodb-panache/runtime/pom.xml b/extensions/panache/mongodb-panache/runtime/pom.xml index b586cc5b1a193..ef61e86e16c9c 100644 --- a/extensions/panache/mongodb-panache/runtime/pom.xml +++ b/extensions/panache/mongodb-panache/runtime/pom.xml @@ -70,7 +70,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/panache/panache-common/deployment/pom.xml b/extensions/panache/panache-common/deployment/pom.xml index ef1a0737cadca..d68ec60715fbe 100644 --- a/extensions/panache/panache-common/deployment/pom.xml +++ b/extensions/panache/panache-common/deployment/pom.xml @@ -25,7 +25,7 @@ quarkus-arc-deployment - org.jboss + io.smallrye jandex diff --git a/extensions/panache/panache-hibernate-common/deployment/pom.xml b/extensions/panache/panache-hibernate-common/deployment/pom.xml index fcc44e98545af..6592af6cd1c03 100644 --- a/extensions/panache/panache-hibernate-common/deployment/pom.xml +++ b/extensions/panache/panache-hibernate-common/deployment/pom.xml @@ -25,7 +25,7 @@ quarkus-arc-deployment - org.jboss + io.smallrye jandex diff --git a/extensions/picocli/runtime/pom.xml b/extensions/picocli/runtime/pom.xml index ea7fc88598ff3..2691817ad14a0 100644 --- a/extensions/picocli/runtime/pom.xml +++ b/extensions/picocli/runtime/pom.xml @@ -35,7 +35,7 @@ quarkus-extension-maven-plugin - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/geo/GeoSearchArgs.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/geo/GeoSearchArgs.java index 2070507f362c3..29b2cd9140f56 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/geo/GeoSearchArgs.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/geo/GeoSearchArgs.java @@ -56,7 +56,7 @@ public GeoSearchArgs fromMember(V member) { * @param latitude the latitude * @return the current {@code GeoSearchArgs} */ - private GeoSearchArgs fromCoordinate(double longitude, double latitude) { + public GeoSearchArgs fromCoordinate(double longitude, double latitude) { this.longitude = longitude; this.latitude = latitude; return this; diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/GeoCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/GeoCommandsTest.java index 0d2a365de53ae..ea34b4ef21c71 100644 --- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/GeoCommandsTest.java +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/GeoCommandsTest.java @@ -494,6 +494,17 @@ void geosearchWithArgs() { assertThat(gv.geohash).isEmpty(); }); + args = new GeoSearchArgs().fromCoordinate(CRUSSOL_LONGITUDE, CRUSSOL_LATITUDE) + .byRadius(5, GeoUnit.KM).withCoordinates().withDistance().descending(); + places = geo.geosearch(key, args); + assertThat(places).hasSize(1).allSatisfy(gv -> { + assertThat(gv.member).isEqualTo(Place.crussol); + assertThat(gv.longitude).isNotEmpty(); + assertThat(gv.latitude).isNotEmpty(); + assertThat(gv.distance).isNotEmpty(); + assertThat(gv.geohash).isEmpty(); + }); + } @Test diff --git a/extensions/resteasy-classic/rest-client-config/runtime/pom.xml b/extensions/resteasy-classic/rest-client-config/runtime/pom.xml index 4d68b76280d4c..24efe5c74c332 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/pom.xml +++ b/extensions/resteasy-classic/rest-client-config/runtime/pom.xml @@ -28,7 +28,7 @@ microprofile-rest-client-api - org.jboss + io.smallrye jandex diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index cbbaffa9befb2..edf3f2cbe5885 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -1610,6 +1610,7 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand case VOID: case TYPE_VARIABLE: case UNRESOLVED_TYPE_VARIABLE: + case TYPE_VARIABLE_REFERENCE: case WILDCARD_TYPE: throw new IllegalArgumentException("Unsupported multipart form field type: " + fieldType + " in " + "field class " + formClassType.name()); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java index 176c3721cdd70..738cc489ee0ea 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java @@ -445,7 +445,7 @@ private String mockInterface(ClassInfo declaringClass, BuildProducer maybeTrustStore = oneOf(clientConfigByClassName().trustStore, clientConfigByConfigKey().trustStore, configRoot.trustStore); @@ -249,7 +249,7 @@ private void registerKeyStore(String keyStorePath, RestClientBuilder builder) { } } - private void registerTrustStore(String trustStorePath, RestClientBuilder builder) { + private void registerTrustStore(String trustStorePath, RestClientBuilderImpl builder) { Optional maybeTrustStorePassword = oneOf(clientConfigByClassName().trustStorePassword, clientConfigByConfigKey().trustStorePassword, configRoot.trustStorePassword); Optional maybeTrustStoreType = oneOf(clientConfigByClassName().trustStoreType, @@ -269,7 +269,7 @@ private void registerTrustStore(String trustStorePath, RestClientBuilder builder e); } - builder.trustStore(trustStore); + builder.trustStore(trustStore, password); } catch (KeyStoreException e) { throw new IllegalArgumentException("Failed to initialize trust store from " + trustStorePath, e); } diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java index 60f80120fb6a7..9a0ec4493673d 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java @@ -109,7 +109,7 @@ public void testClientSpecificConfigs() { Mockito.verify(restClientBuilderMock).register(MyResponseFilter1.class); Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.COMMA_SEPARATED); - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); + Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } @@ -151,7 +151,7 @@ public void testGlobalConfigs() { Mockito.verify(restClientBuilderMock).register(MyResponseFilter2.class); Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.MULTI_PAIRS); - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); + Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/OverridableIndex.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/OverridableIndex.java index 1c17b0f415891..fcb4b35f10a2b 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/OverridableIndex.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/OverridableIndex.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Comparator; +import java.util.HashSet; import java.util.Set; import java.util.TreeSet; @@ -52,6 +53,18 @@ public Collection getAllKnownSubclasses(DotName dn) { return overrideCollection(original.getAllKnownSubclasses(dn), override.getAllKnownSubclasses(dn), classInfoComparator); } + @Override + public Collection getKnownDirectSubinterfaces(DotName dn) { + return overrideCollection(original.getKnownDirectSubinterfaces(dn), override.getKnownDirectSubinterfaces(dn), + classInfoComparator); + } + + @Override + public Collection getAllKnownSubinterfaces(DotName dn) { + return overrideCollection(original.getAllKnownSubinterfaces(dn), override.getAllKnownSubinterfaces(dn), + classInfoComparator); + } + @Override public Collection getKnownDirectImplementors(DotName dn) { return overrideCollection(original.getKnownDirectImplementors(dn), override.getKnownDirectImplementors(dn), @@ -90,6 +103,17 @@ public Collection getKnownUsers(DotName dn) { return overrideCollection(original.getKnownUsers(dn), override.getKnownUsers(dn), classInfoComparator); } + @Override + public Collection getClassesInPackage(DotName pn) { + return overrideCollection(original.getClassesInPackage(pn), override.getClassesInPackage(pn), classInfoComparator); + } + + @Override + public Set getSubpackages(DotName pn) { + return new HashSet<>(overrideCollection(original.getSubpackages(pn), override.getSubpackages(pn), + Comparator.naturalOrder())); + } + private Comparator classInfoComparator = new Comparator() { @Override public int compare(ClassInfo t, ClassInfo t1) { diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml b/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml index 60bd5b514e3af..ecd53ab12ed09 100644 --- a/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml +++ b/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml @@ -47,20 +47,6 @@ org.apache.activemq artemis-server test - - - org.jboss.logmanager - jboss-logmanager - - - commons-logging - commons-logging - - - jakarta.json - jakarta.json-api - - org.testcontainers @@ -123,5 +109,4 @@ - diff --git a/extensions/smallrye-stork/runtime/src/main/java/io/quarkus/stork/StorkConfigUtil.java b/extensions/smallrye-stork/runtime/src/main/java/io/quarkus/stork/StorkConfigUtil.java index db9d9f68b29a8..a87bb3f39b0f5 100644 --- a/extensions/smallrye-stork/runtime/src/main/java/io/quarkus/stork/StorkConfigUtil.java +++ b/extensions/smallrye-stork/runtime/src/main/java/io/quarkus/stork/StorkConfigUtil.java @@ -16,9 +16,9 @@ public static List toStorkServiceConfig(StorkConfiguration storkC for (String serviceName : servicesConfigs) { builder.setServiceName(serviceName); ServiceConfiguration serviceConfiguration = storkConfiguration.serviceConfiguration.get(serviceName); - SimpleServiceConfig.SimpleServiceDiscoveryConfig storkServiceDiscoveryConfig = new SimpleServiceConfig.SimpleServiceDiscoveryConfig( + SimpleServiceConfig.SimpleConfigWithType storkServiceDiscoveryConfig = new SimpleServiceConfig.SimpleConfigWithType( serviceConfiguration.serviceDiscovery.type, serviceConfiguration.serviceDiscovery.params); - builder.setServiceDiscovery(storkServiceDiscoveryConfig); + builder = builder.setServiceDiscovery(storkServiceDiscoveryConfig); SimpleServiceConfig.SimpleLoadBalancerConfig loadBalancerConfig = new SimpleServiceConfig.SimpleLoadBalancerConfig( serviceConfiguration.loadBalancer.type, serviceConfiguration.loadBalancer.parameters); builder.setLoadBalancer(loadBalancerConfig); diff --git a/extensions/spring-boot-properties/runtime/pom.xml b/extensions/spring-boot-properties/runtime/pom.xml index eb83c846666e6..88d6eb8da01e9 100644 --- a/extensions/spring-boot-properties/runtime/pom.xml +++ b/extensions/spring-boot-properties/runtime/pom.xml @@ -41,7 +41,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/extensions/spring-di/deployment/src/test/java/io/quarkus/spring/di/deployment/SpringDIProcessorTest.java b/extensions/spring-di/deployment/src/test/java/io/quarkus/spring/di/deployment/SpringDIProcessorTest.java index 74eccaef4e87b..5791891b3fca2 100644 --- a/extensions/spring-di/deployment/src/test/java/io/quarkus/spring/di/deployment/SpringDIProcessorTest.java +++ b/extensions/spring-di/deployment/src/test/java/io/quarkus/spring/di/deployment/SpringDIProcessorTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; -import java.io.InputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -26,8 +25,8 @@ import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; import org.jboss.jandex.IndexView; -import org.jboss.jandex.Indexer; import org.jboss.jandex.MethodInfo; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -39,7 +38,6 @@ import io.quarkus.arc.processor.BeanArchives; import io.quarkus.arc.processor.DotNames; -import io.quarkus.deployment.util.IoUtil; /** * @author Brent Douglas @@ -205,17 +203,12 @@ public void getAnnotationsToAddBeanMethodWithScope() { } private IndexView getIndex(final Class... classes) { - final Indexer indexer = new Indexer(); - for (final Class clazz : classes) { - final String className = clazz.getName(); - try (InputStream stream = IoUtil.readClass(getClass().getClassLoader(), className)) { - final ClassInfo beanInfo = indexer.index(stream); - } catch (IOException e) { - throw new IllegalStateException("Failed to index: " + className, e); - } + try { + Index index = Index.of(classes); + return BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(), index); + } catch (IOException e) { + throw new IllegalStateException("Failed to index classes", e); } - return BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(), - indexer.complete()); } @SafeVarargs diff --git a/extensions/spring-scheduled/deployment/src/test/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessorTest.java b/extensions/spring-scheduled/deployment/src/test/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessorTest.java index ad55c1f7ece30..3abb0f2f3334b 100644 --- a/extensions/spring-scheduled/deployment/src/test/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessorTest.java +++ b/extensions/spring-scheduled/deployment/src/test/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessorTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import java.io.InputStream; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -13,16 +12,14 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; -import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; import org.jboss.jandex.IndexView; -import org.jboss.jandex.Indexer; import org.jboss.jandex.MethodInfo; import org.junit.jupiter.api.Test; import org.springframework.scheduling.annotation.Scheduled; import io.quarkus.arc.processor.BeanArchives; -import io.quarkus.deployment.util.IoUtil; public class SpringScheduledProcessorTest { @@ -116,17 +113,12 @@ public void testBuildDelayParamFromInvalidFormat() { } private IndexView getIndex(final Class... classes) { - final Indexer indexer = new Indexer(); - for (final Class clazz : classes) { - final String className = clazz.getName(); - try (InputStream stream = IoUtil.readClass(getClass().getClassLoader(), className)) { - final ClassInfo beanInfo = indexer.index(stream); - } catch (IOException e) { - throw new IllegalStateException("Failed to index: " + className, e); - } + try { + Index index = Index.of(classes); + return BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(), index); + } catch (IOException e) { + throw new IllegalStateException("Failed to index classes", e); } - return BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(), - indexer.complete()); } @ApplicationScoped diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/CustomFormAuthChallengeTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/CustomFormAuthChallengeTest.java new file mode 100644 index 0000000000000..696e130ec95d2 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/CustomFormAuthChallengeTest.java @@ -0,0 +1,108 @@ +package io.quarkus.vertx.http.security; + +import java.util.Set; +import java.util.function.Supplier; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Alternative; +import javax.inject.Inject; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Priority; +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.request.AuthenticationRequest; +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.vertx.http.runtime.security.ChallengeData; +import io.quarkus.vertx.http.runtime.security.FormAuthenticationMechanism; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; +import io.quarkus.vertx.http.runtime.security.HttpCredentialTransport; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +public class CustomFormAuthChallengeTest { + + private static final int EXPECTED_STATUS = 203; + private static final String EXPECTED_HEADER_NAME = "ElizabethII"; + private static final String EXPECTED_HEADER_VALUE = "CharlesIV"; + private static final String ADMIN = "admin"; + private static final String APP_PROPS = "" + + "quarkus.http.auth.form.enabled=true\n" + + "quarkus.http.auth.form.login-page=login\n" + + "quarkus.http.auth.form.error-page=error\n" + + "quarkus.http.auth.form.landing-page=landing\n" + + "quarkus.http.auth.policy.r1.roles-allowed=admin\n" + + "quarkus.http.auth.session.encryption-key=CHANGEIT-CHANGEIT-CHANGEIT-CHANGEIT-CHANGEIT\n"; + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(CustomFormAuthenticator.class, TestIdentityProvider.class, TestIdentityController.class, + TestTrustedIdentityProvider.class, PathHandler.class) + .addAsResource(new StringAsset(APP_PROPS), "application.properties"); + } + }); + + @BeforeAll + public static void setup() { + TestIdentityController.resetRoles().add(ADMIN, ADMIN, ADMIN); + } + + @Test + public void testCustomGetChallengeIsCalled() { + RestAssured + .given() + .when() + .formParam("j_username", ADMIN) + .formParam("j_password", "wrong_password") + .post("/j_security_check") + .then() + .assertThat() + .statusCode(EXPECTED_STATUS) + .header(EXPECTED_HEADER_NAME, Matchers.is(EXPECTED_HEADER_VALUE)); + } + + @Alternative + @Priority(1) + @ApplicationScoped + public static class CustomFormAuthenticator implements HttpAuthenticationMechanism { + + @Inject + FormAuthenticationMechanism delegate; + + @Override + public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) { + final var authenticate = delegate.authenticate(context, identityProviderManager); + context.put(HttpAuthenticationMechanism.class.getName(), this); + return authenticate; + } + + @Override + public Uni getChallenge(RoutingContext context) { + return Uni.createFrom().item(new ChallengeData(EXPECTED_STATUS, EXPECTED_HEADER_NAME, EXPECTED_HEADER_VALUE)); + } + + @Override + public Set> getCredentialTypes() { + return delegate.getCredentialTypes(); + } + + @Override + public Uni getCredentialTransport(RoutingContext context) { + return delegate.getCredentialTransport(context); + } + } + +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java index 0a2ce19a3343d..8b2de1d915bd1 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java @@ -137,10 +137,14 @@ public Uni sendChallenge(RoutingContext routingContext) { routingContext.request().resume(); Uni result = null; - HttpAuthenticationMechanism matchingMech = routingContext.get(HttpAuthenticationMechanism.class.getName()); - if (matchingMech != null) { - result = matchingMech.sendChallenge(routingContext); + // we only require auth mechanism to put itself into routing context when there is more than one mechanism registered + if (mechanisms.length > 1) { + HttpAuthenticationMechanism matchingMech = routingContext.get(HttpAuthenticationMechanism.class.getName()); + if (matchingMech != null) { + result = matchingMech.sendChallenge(routingContext); + } } + if (result == null) { result = mechanisms[0].sendChallenge(routingContext); for (int i = 1; i < mechanisms.length; ++i) { @@ -169,9 +173,12 @@ public Uni apply(Boolean authDone) { } public Uni getChallenge(RoutingContext routingContext) { - HttpAuthenticationMechanism matchingMech = routingContext.get(HttpAuthenticationMechanism.class.getName()); - if (matchingMech != null) { - return matchingMech.getChallenge(routingContext); + // we only require auth mechanism to put itself into routing context when there is more than one mechanism registered + if (mechanisms.length > 1) { + HttpAuthenticationMechanism matchingMech = routingContext.get(HttpAuthenticationMechanism.class.getName()); + if (matchingMech != null) { + return matchingMech.getChallenge(routingContext); + } } Uni result = mechanisms[0].getChallenge(routingContext); for (int i = 1; i < mechanisms.length; ++i) { diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusConsumer.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusConsumer.java index 046ef6886e5e4..a4069c07d4609 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusConsumer.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusConsumer.java @@ -169,14 +169,14 @@ private static void implementInvoke(BeanInfo bean, MethodInfo method, ClassCreat // https://github.com/quarkusio/quarkus/issues/21621 Optional headerType = Optional.empty(); Type paramType; - if (method.parameters().size() == 2) { - Type firstParamType = method.parameters().get(0); + if (method.parametersCount() == 2) { + Type firstParamType = method.parameterType(0); if (VertxConstants.isMessageHeaders(firstParamType.name())) { headerType = Optional.of(firstParamType); } - paramType = method.parameters().get(1); + paramType = method.parameterType(1); } else { - paramType = method.parameters().get(0); + paramType = method.parameterType(0); } if (paramType.name().equals(MESSAGE)) { // io.vertx.core.eventbus.Message diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java index 43b5c21d71156..7eeb82fb9f917 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java @@ -125,7 +125,7 @@ void collectEventConsumers( AnnotationInstance consumeEvent = annotationStore.getAnnotation(method, CONSUME_EVENT); if (consumeEvent != null) { // Validate method params and return type - List params = method.parameters(); + List params = method.parameterTypes(); if (params.size() == 2) { if (!isMessageHeaders(params.get(0).name())) { // If there are two parameters, the first must be message headers. diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index ea45249f5d2db..9838bd1a6cf34 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -42,13 +42,13 @@ 2.0.2 1.3.3 - 2.4.3.Final + 3.0.0 5.9.0 3.8.6 3.23.1 3.5.0.Final 1.3.5 - 1.1.0.Final + 1.2.0.Final 2.2.3 1.6.0 @@ -85,7 +85,7 @@ - org.jboss + io.smallrye jandex ${version.jandex} diff --git a/independent-projects/arc/processor/pom.xml b/independent-projects/arc/processor/pom.xml index 84e264d8f9208..c0411c79cb570 100644 --- a/independent-projects/arc/processor/pom.xml +++ b/independent-projects/arc/processor/pom.xml @@ -31,7 +31,7 @@ - org.jboss + io.smallrye jandex diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AsmUtilCopy.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AsmUtilCopy.java index f5835c0fde19c..6b60e5587e664 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AsmUtilCopy.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AsmUtilCopy.java @@ -9,6 +9,8 @@ import org.jboss.jandex.PrimitiveType.Primitive; import org.jboss.jandex.Type; import org.jboss.jandex.TypeVariable; +import org.jboss.jandex.TypeVariableReference; +import org.jboss.jandex.UnresolvedTypeVariable; /** * Copy of quarkus-core AsmUtil for some methods, with a tweak on the ARG_MAPPER (not name->String anymore) and @@ -146,8 +148,14 @@ else if (erased) sb.append("T").append(typeVariable.identifier()).append(";"); break; case UNRESOLVED_TYPE_VARIABLE: - // FIXME: ?? + // TODO no mapping, no support for "erased" + UnresolvedTypeVariable unresolvedTypeVariable = type.asUnresolvedTypeVariable(); + sb.append("T").append(unresolvedTypeVariable.identifier()).append(";"); break; + case TYPE_VARIABLE_REFERENCE: + // TODO no mapping, no support for "erased" + TypeVariableReference typeVariableReference = type.asTypeVariableReference(); + sb.append("T").append(typeVariableReference.identifier()).append(";"); case VOID: sb.append("V"); break; @@ -243,6 +251,7 @@ private static boolean needsSignature(Type type) { case PARAMETERIZED_TYPE: case TYPE_VARIABLE: case UNRESOLVED_TYPE_VARIABLE: + case TYPE_VARIABLE_REFERENCE: case WILDCARD_TYPE: return true; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanArchives.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanArchives.java index e54908eb06fee..18f24553f034f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanArchives.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanArchives.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -17,7 +18,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Queue; import java.util.Set; import javax.enterprise.context.BeforeDestroyed; import javax.enterprise.context.Destroyed; @@ -144,6 +147,73 @@ public Collection getAllKnownSubclasses(DotName className) { return allKnown; } + private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { + final Set subClassesToProcess = new HashSet(); + subClassesToProcess.add(className); + while (!subClassesToProcess.isEmpty()) { + final Iterator toProcess = subClassesToProcess.iterator(); + DotName name = toProcess.next(); + toProcess.remove(); + processedClasses.add(name); + getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + } + } + + private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, + Set processedClasses) { + final Collection directSubclasses = getKnownDirectSubclasses(name); + if (directSubclasses != null) { + for (final ClassInfo clazz : directSubclasses) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + allKnown.add(clazz); + subClassesToProcess.add(className); + } + } + } + } + + @Override + public Collection getKnownDirectSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getKnownDirectSubinterfaces(interfaceName); + } + Set directSubinterfaces = new HashSet<>(index.getKnownDirectSubinterfaces(interfaceName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isPresent() && additional.get().interfaceNames().contains(interfaceName)) { + directSubinterfaces.add(additional.get()); + } + } + return directSubinterfaces; + } + + @Override + public Collection getAllKnownSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getAllKnownSubinterfaces(interfaceName); + } + + Set result = new HashSet<>(); + + Queue workQueue = new ArrayDeque<>(); + Set alreadyProcessed = new HashSet<>(); + + workQueue.add(interfaceName); + while (!workQueue.isEmpty()) { + DotName iface = workQueue.remove(); + if (!alreadyProcessed.add(iface)) { + continue; + } + + for (ClassInfo directSubinterface : getKnownDirectSubinterfaces(iface)) { + result.add(directSubinterface); + workQueue.add(directSubinterface.name()); + } + } + + return result; + } + @Override public Collection getKnownDirectImplementors(DotName className) { if (additionalClasses.isEmpty()) { @@ -183,6 +253,27 @@ public Collection getAllKnownImplementors(DotName interfaceName) { return allKnown; } + private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, + Set processedClasses) { + final Collection list = getKnownDirectImplementors(name); + if (list != null) { + for (final ClassInfo clazz : list) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + if (Modifier.isInterface(clazz.flags())) { + subInterfacesToProcess.add(className); + } else { + if (!allKnown.contains(clazz)) { + allKnown.add(clazz); + processedClasses.add(className); + getAllKnownSubClasses(className, allKnown, processedClasses); + } + } + } + } + } + } + @Override public Collection getAnnotations(DotName annotationName) { return index.getAnnotations(annotationName); @@ -208,51 +299,44 @@ public Collection getKnownUsers(DotName className) { return this.index.getKnownUsers(className); } - private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { - final Set subClassesToProcess = new HashSet(); - subClassesToProcess.add(className); - while (!subClassesToProcess.isEmpty()) { - final Iterator toProcess = subClassesToProcess.iterator(); - DotName name = toProcess.next(); - toProcess.remove(); - processedClasses.add(name); - getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + @Override + public Collection getClassesInPackage(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getClassesInPackage(packageName); } - } - - private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, - Set processedClasses) { - final Collection directSubclasses = getKnownDirectSubclasses(name); - if (directSubclasses != null) { - for (final ClassInfo clazz : directSubclasses) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - allKnown.add(clazz); - subClassesToProcess.add(className); - } + Set classesInPackage = new HashSet<>(index.getClassesInPackage(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + if (Objects.equals(packageName, additional.get().name().packagePrefixName())) { + classesInPackage.add(additional.get()); } } + return classesInPackage; } - private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, - Set processedClasses) { - final Collection list = getKnownDirectImplementors(name); - if (list != null) { - for (final ClassInfo clazz : list) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - if (Modifier.isInterface(clazz.flags())) { - subInterfacesToProcess.add(className); - } else { - if (!allKnown.contains(clazz)) { - allKnown.add(clazz); - processedClasses.add(className); - getAllKnownSubClasses(className, allKnown, processedClasses); - } - } + @Override + public Set getSubpackages(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getSubpackages(packageName); + } + Set subpackages = new HashSet<>(index.getSubpackages(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + DotName pkg = additional.get().name().packagePrefixName(); + while (pkg != null) { + DotName superPkg = pkg.packagePrefixName(); + if (superPkg != null && superPkg.equals(packageName)) { + subpackages.add(pkg); } + pkg = superPkg; } + } + return subpackages; } private Optional computeAdditional(DotName className) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index 3523648b96b93..e13c36fcebb3b 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -35,7 +35,6 @@ import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; -import org.jboss.jandex.TypeVariable; /** * @@ -120,7 +119,7 @@ Collection generate(BeanInfo bean, String beanClassName, if (!providerClass.typeParameters().isEmpty()) { clientProxy.setSignature(AsmUtilCopy.getGeneratedSubClassSignature(providerClass, bean.getProviderType())); } - Map> resolvedTypeVariables = Types.resolvedTypeVariables(providerClass, + Map> resolvedTypeVariables = Types.resolvedTypeVariables(providerClass, bean.getDeployment()); FieldCreator beanField = clientProxy.getFieldCreator(BEAN_FIELD, InjectableBean.class) .setModifiers(ACC_PRIVATE | ACC_FINAL); @@ -148,10 +147,10 @@ Collection generate(BeanInfo bean, String beanClassName, MethodDescriptor originalMethodDescriptor = MethodDescriptor.of(method); MethodCreator forward = clientProxy.getMethodCreator(originalMethodDescriptor); if (AsmUtilCopy.needsSignature(method)) { - Map methodClassVariables = resolvedTypeVariables.get(method.declaringClass()); + Map methodClassVariables = resolvedTypeVariables.get(method.declaringClass()); String signature = AsmUtilCopy.getSignature(method, typeVariable -> { if (methodClassVariables != null) { - Type ret = methodClassVariables.get(typeVariable); + Type ret = methodClassVariables.get(typeVariable.identifier()); // let's not map a TV to itself if (ret != typeVariable) return ret; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorGenerator.java index 65fc970d8fece..6091bad566cc4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorGenerator.java @@ -187,7 +187,7 @@ private String generateDecoratorImplementation(String baseName, String targetPac // A decorated type can declare type parameters // For example Converter should result in a T -> String mapping List typeParameters = decoratedTypeClass.typeParameters(); - Map resolvedTypeParameters = Collections.emptyMap(); + Map resolvedTypeParameters = Collections.emptyMap(); if (!typeParameters.isEmpty()) { resolvedTypeParameters = new HashMap<>(); // The delegate type can be used to infer the parameter types @@ -195,7 +195,7 @@ private String generateDecoratorImplementation(String baseName, String targetPac if (type.kind() == Kind.PARAMETERIZED_TYPE) { List typeArguments = type.asParameterizedType().arguments(); for (int i = 0; i < typeParameters.size(); i++) { - resolvedTypeParameters.put(typeParameters.get(i), typeArguments.get(i)); + resolvedTypeParameters.put(typeParameters.get(i).identifier(), typeArguments.get(i)); } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java index 1477f06aceedd..2bdf5ea705f41 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java @@ -22,7 +22,6 @@ import org.jboss.jandex.MethodInfo; import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; -import org.jboss.jandex.TypeVariable; /** * Represents an injection point. @@ -262,15 +261,15 @@ private static Type resolveType(Type type, ClassInfo beanClass, ClassInfo declar if (type.kind() == org.jboss.jandex.Type.Kind.CLASS) { return type; } - Map> resolvedTypeVariables = Types.resolvedTypeVariables(beanClass, beanDeployment); + Map> resolvedTypeVariables = Types.resolvedTypeVariables(beanClass, beanDeployment); return resolveType(type, declaringClass, beanDeployment, resolvedTypeVariables); } private static Type resolveType(Type type, ClassInfo beanClass, BeanDeployment beanDeployment, - Map> resolvedTypeVariables) { + Map> resolvedTypeVariables) { if (type.kind() == org.jboss.jandex.Type.Kind.TYPE_VARIABLE) { if (resolvedTypeVariables.containsKey(beanClass)) { - return resolvedTypeVariables.get(beanClass).getOrDefault(type.asTypeVariable(), type); + return resolvedTypeVariables.get(beanClass).getOrDefault(type.asTypeVariable().identifier(), type); } } else if (type.kind() == org.jboss.jandex.Type.Kind.PARAMETERIZED_TYPE) { ParameterizedType parameterizedType = type.asParameterizedType(); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index 0095e82894e5d..623dc14c5589a 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -438,6 +438,7 @@ static DotName toRawType(Type a) { return a.asParameterizedType().name(); case TYPE_VARIABLE: case UNRESOLVED_TYPE_VARIABLE: + case TYPE_VARIABLE_REFERENCE: case WILDCARD_TYPE: default: return DotNames.OBJECT; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java index 6a4579e675011..b858c74df729e 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java @@ -27,7 +27,6 @@ import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; import org.jboss.jandex.Type; -import org.jboss.jandex.TypeVariable; import org.jboss.logging.Logger; /** @@ -54,7 +53,7 @@ static ObserverInfo create(BeanInfo declaringBean, MethodInfo observerMethod, In Type observedType = observerMethod.parameterType(eventParameter.position()); if (Types.containsTypeVariable(observedType)) { - Map resolvedTypeVariables = Types + Map resolvedTypeVariables = Types .resolvedTypeVariables(declaringBean.getImplClazz(), declaringBean.getDeployment()) .getOrDefault(observerMethod.declaringClass(), Collections.emptyMap()); observedType = Types.resolveTypeParam(observedType, resolvedTypeVariables, diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java index 08462d17a133b..d385a31f874c0 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java @@ -480,7 +480,7 @@ private void processDecorator(DecoratorInfo decorator, BeanInfo bean, Type provi // The delegate type can declare type parameters // For example @Delegate Converter should result in a T -> String mapping List typeParameters = delegateTypeClass.typeParameters(); - Map resolvedTypeParameters = Collections.emptyMap(); + Map resolvedTypeParameters = Collections.emptyMap(); if (!typeParameters.isEmpty()) { resolvedTypeParameters = new HashMap<>(); // The delegate type can be used to infer the parameter types @@ -488,7 +488,7 @@ private void processDecorator(DecoratorInfo decorator, BeanInfo bean, Type provi if (delegateType.kind() == Kind.PARAMETERIZED_TYPE) { List typeArguments = delegateType.asParameterizedType().arguments(); for (int i = 0; i < typeParameters.size(); i++) { - resolvedTypeParameters.put(typeParameters.get(i), typeArguments.get(i)); + resolvedTypeParameters.put(typeParameters.get(i).identifier(), typeArguments.get(i)); } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index c604471397b4b..a17f01ed58dbb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -224,8 +224,8 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat default: throw new IllegalArgumentException("Unsupported primitive type: " + type); } - } else if (Kind.UNRESOLVED_TYPE_VARIABLE.equals(type.kind())) { - String identifier = type.asUnresolvedTypeVariable().identifier(); + } else if (Kind.TYPE_VARIABLE_REFERENCE.equals(type.kind())) { + String identifier = type.asTypeVariableReference().identifier(); TypeVariableInfo recursive = TypeVariableInfo.find(identifier, typeVariableStack); if (recursive != null) { ResultHandle typeVariableHandle = creator.newInstance( @@ -384,7 +384,7 @@ static List getResolvedParameters(ClassInfo classInfo, MethodInfo method, return getResolvedParameters(classInfo, Collections.emptyMap(), method, index); } - static List getResolvedParameters(ClassInfo classInfo, Map resolvedMap, + static List getResolvedParameters(ClassInfo classInfo, Map resolvedMap, MethodInfo method, IndexView index) { List typeParameters = classInfo.typeParameters(); // E.g. Foo, T, List @@ -414,12 +414,13 @@ static List getResolvedParameters(ClassInfo classInfo, Map getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFieldOrMethod, - Map resolvedTypeParameters, - BeanDeployment beanDeployment, BiConsumer> resolvedTypeVariablesConsumer) { + Map resolvedTypeParameters, + BeanDeployment beanDeployment, BiConsumer> resolvedTypeVariablesConsumer) { Set types = new HashSet<>(); List typeParameters = classInfo.typeParameters(); - if (typeParameters.isEmpty() || !typeParameters.stream().allMatch(resolvedTypeParameters::containsKey)) { + if (typeParameters.isEmpty() + || !typeParameters.stream().allMatch(it -> resolvedTypeParameters.containsKey(it.identifier()))) { // Not a parameterized type or a raw type types.add(Type.create(classInfo.name(), Kind.CLASS)); } else { @@ -427,7 +428,7 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi Type[] typeParams = new Type[typeParameters.size()]; boolean skipThisType = false; for (int i = 0; i < typeParameters.size(); i++) { - typeParams[i] = resolvedTypeParameters.get(typeParameters.get(i)); + typeParams[i] = resolvedTypeParameters.get(typeParameters.get(i).identifier()); // this should only be the case for producers; wildcard is not a legal bean type // see https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#legal_bean_types if (typeParams[i].kind().equals(Kind.WILDCARD_TYPE) && producerFieldOrMethod != null) { @@ -440,9 +441,9 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi } } if (resolvedTypeVariablesConsumer != null) { - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); for (int i = 0; i < typeParameters.size(); i++) { - resolved.put(typeParameters.get(i), typeParams[i]); + resolved.put(typeParameters.get(i).identifier(), typeParams[i]); } resolvedTypeVariablesConsumer.accept(classInfo, resolved); } @@ -457,7 +458,7 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi } ClassInfo interfaceClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), interfaceType.name()); if (interfaceClassInfo != null) { - Map resolved = Collections.emptyMap(); + Map resolved = Collections.emptyMap(); if (Kind.PARAMETERIZED_TYPE.equals(interfaceType.kind())) { resolved = buildResolvedMap(interfaceType.asParameterizedType().arguments(), interfaceClassInfo.typeParameters(), resolvedTypeParameters, beanDeployment.getBeanArchiveIndex()); @@ -470,7 +471,7 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi if (classInfo.superClassType() != null) { ClassInfo superClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClassInfo != null) { - Map resolved = Collections.emptyMap(); + Map resolved = Collections.emptyMap(); if (Kind.PARAMETERIZED_TYPE.equals(classInfo.superClassType().kind())) { resolved = buildResolvedMap(classInfo.superClassType().asParameterizedType().arguments(), superClassInfo.typeParameters(), @@ -509,9 +510,9 @@ static Set getDelegateTypeClosure(InjectionPointInfo delegateInjectionPoin return types; } - static Map> resolvedTypeVariables(ClassInfo classInfo, + static Map> resolvedTypeVariables(ClassInfo classInfo, BeanDeployment beanDeployment) { - Map> resolvedTypeVariables = new HashMap<>(); + Map> resolvedTypeVariables = new HashMap<>(); getTypeClosure(classInfo, null, Collections.emptyMap(), beanDeployment, resolvedTypeVariables::put); return resolvedTypeVariables; } @@ -540,26 +541,27 @@ static Set restrictBeanTypes(Set types, Collection Map buildResolvedMap(List resolvedArguments, + static Map buildResolvedMap(List resolvedArguments, List typeVariables, - Map resolvedTypeParameters, IndexView index) { - Map resolvedMap = new HashMap<>(); + Map resolvedTypeParameters, IndexView index) { + Map resolvedMap = new HashMap<>(); for (int i = 0; i < resolvedArguments.size(); i++) { - resolvedMap.put(typeVariables.get(i), resolveTypeParam(resolvedArguments.get(i), resolvedTypeParameters, index)); + resolvedMap.put(typeVariables.get(i).identifier(), + resolveTypeParam(resolvedArguments.get(i), resolvedTypeParameters, index)); } return resolvedMap; } - static Type resolveTypeParam(Type typeParam, Map resolvedTypeParameters, IndexView index) { + static Type resolveTypeParam(Type typeParam, Map resolvedTypeParameters, IndexView index) { if (typeParam.kind() == Kind.TYPE_VARIABLE) { - return resolvedTypeParameters.getOrDefault(typeParam, typeParam); + return resolvedTypeParameters.getOrDefault(typeParam.asTypeVariable().identifier(), typeParam); } else if (typeParam.kind() == Kind.PARAMETERIZED_TYPE) { ParameterizedType parameterizedType = typeParam.asParameterizedType(); ClassInfo classInfo = getClassByName(index, parameterizedType.name()); if (classInfo != null) { List typeParameters = classInfo.typeParameters(); List arguments = parameterizedType.arguments(); - Map resolvedMap = buildResolvedMap(arguments, typeParameters, + Map resolvedMap = buildResolvedMap(arguments, typeParameters, resolvedTypeParameters, index); Type[] typeParams = new Type[typeParameters.size()]; for (int i = 0; i < typeParameters.size(); i++) { diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java index 787085e203cb2..b24c18e3cba06 100644 --- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java @@ -18,7 +18,6 @@ import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; -import org.jboss.jandex.TypeVariable; import org.junit.jupiter.api.Test; public class TypesTest { @@ -31,7 +30,7 @@ public void testGetTypeClosure() throws IOException { DotName fooName = DotName.createSimple(Foo.class.getName()); DotName producerName = DotName.createSimple(Producer.class.getName()); ClassInfo fooClass = index.getClassByName(fooName); - Map> resolvedTypeVariables = new HashMap<>(); + Map> resolvedTypeVariables = new HashMap<>(); BeanDeployment dummyDeployment = BeanProcessor.builder().setBeanArchiveIndex(index).build().getBeanDeployment(); // Baz, Foo, Object @@ -46,7 +45,7 @@ public void testGetTypeClosure() throws IOException { null))); assertEquals(resolvedTypeVariables.size(), 1); assertTrue(resolvedTypeVariables.containsKey(fooClass)); - assertEquals(resolvedTypeVariables.get(fooClass).get(fooClass.typeParameters().get(0)), + assertEquals(resolvedTypeVariables.get(fooClass).get(fooClass.typeParameters().get(0).identifier()), Type.create(DotName.createSimple(String.class.getName()), Kind.CLASS)); resolvedTypeVariables.clear(); diff --git a/independent-projects/bootstrap/app-model/pom.xml b/independent-projects/bootstrap/app-model/pom.xml index ed97e4bac6473..937d0784c806f 100644 --- a/independent-projects/bootstrap/app-model/pom.xml +++ b/independent-projects/bootstrap/app-model/pom.xml @@ -86,7 +86,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 1a6374d70b395..0fac28e2f3ab4 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -36,7 +36,7 @@ 11 3.0.0-M7 1.6.8 - 1.2.3 + 3.0.0 3.23.1 @@ -83,9 +83,9 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin - ${jandex-maven-plugin.version} + ${jandex.version} maven-javadoc-plugin diff --git a/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml b/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml index 805b934d866db..30f9458575ccd 100644 --- a/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml +++ b/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml @@ -100,6 +100,8 @@ org.jboss.modules:jboss-modules org.javassist:javassist + + org.jboss:jandex diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java index 81b8502db1072..1f11ea46fe198 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java @@ -278,13 +278,14 @@ public CompletionStage getAsync(String key) { } } // Iteration metadata + final int count = index + 1; switch (key) { case "count": - return CompletedStage.of(index + 1); + return CompletedStage.of(count); case "index": return CompletedStage.of(index); case "indexParity": - return index % 2 != 0 ? EVEN : ODD; + return count % 2 == 0 ? EVEN : ODD; case "hasNext": return hasNext ? Results.TRUE : Results.FALSE; case "isLast": @@ -293,10 +294,10 @@ public CompletionStage getAsync(String key) { return index == 0 ? Results.TRUE : Results.FALSE; case "isOdd": case "odd": - return (index % 2 == 0) ? Results.TRUE : Results.FALSE; + return count % 2 != 0 ? Results.TRUE : Results.FALSE; case "isEven": case "even": - return (index % 2 != 0) ? Results.TRUE : Results.FALSE; + return count % 2 == 0 ? Results.TRUE : Results.FALSE; default: return Results.notFound(key); } diff --git a/independent-projects/qute/generator/pom.xml b/independent-projects/qute/generator/pom.xml index e0b861f993875..25eb37333cf27 100644 --- a/independent-projects/qute/generator/pom.xml +++ b/independent-projects/qute/generator/pom.xml @@ -23,7 +23,7 @@ jboss-logging - org.jboss + io.smallrye jandex diff --git a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java index 3dfeebfea4bc7..e4257de003936 100644 --- a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java +++ b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java @@ -994,6 +994,7 @@ private boolean skipMemberType(Type type) { case ARRAY: case TYPE_VARIABLE: case UNRESOLVED_TYPE_VARIABLE: + case TYPE_VARIABLE_REFERENCE: case WILDCARD_TYPE: return true; default: diff --git a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java index 5784b8a310771..e0a9a012fcc94 100644 --- a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java +++ b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java @@ -143,6 +143,28 @@ public void testWithEngine() throws Exception { assertEquals("10", engine.parse("{io_quarkus_qute_generator_MyService:getDummy(5)}").render()); } + @Test + public void testArrays() { + Engine engine = Engine.builder().addDefaults().build(); + assertEquals("1,2,3,4,5,6,7,8,9,10,", engine.parse("{#for i in 10}{i_count},{/for}").render()); + assertEquals("0,1,2,3,4,5,6,7,8,9,", engine.parse("{#for i in 10}{i_index},{/for}").render()); + assertEquals("odd,even,odd,even,odd,even,odd,even,odd,even,", + engine.parse("{#for i in 10}{i_indexParity},{/for}").render()); + assertEquals("true,false,true,false,true,", + engine.parse("{#for i in 5}{i_odd},{/for}").render()); + assertEquals("false,true,false,true,false,", + engine.parse("{#for i in 5}{i_even},{/for}").render()); + { //these two are not documented in the guide (https://quarkus.io/guides/qute-reference) + assertEquals("true,false,true,false,true,", + engine.parse("{#for i in 5}{i_isOdd},{/for}").render()); + assertEquals("false,true,false,true,false,", + engine.parse("{#for i in 5}{i_isEven},{/for}").render()); + } + assertEquals("true,true,true,true,false,", engine.parse("{#for i in 5}{i_hasNext},{/for}").render()); + assertEquals("false,false,false,false,true,", engine.parse("{#for i in 5}{i_isLast},{/for}").render()); + assertEquals("true,false,false,false,false,", engine.parse("{#for i in 5}{i_isFirst},{/for}").render()); + } + private Resolver newResolver(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index 0fa65445960e5..b6c9de8348830 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -42,8 +42,8 @@ 11 5.9.0 3.23.1 - 2.4.3.Final - 1.1.0.Final + 3.0.0 + 1.2.0.Final 3.5.0.Final 3.0.0-M7 1.6.8 @@ -86,7 +86,7 @@ ${version.jboss-logging} - org.jboss + io.smallrye jandex ${version.jandex} diff --git a/independent-projects/resteasy-reactive/client/processor/pom.xml b/independent-projects/resteasy-reactive/client/processor/pom.xml index dd20ccce3997c..513306efc372b 100644 --- a/independent-projects/resteasy-reactive/client/processor/pom.xml +++ b/independent-projects/resteasy-reactive/client/processor/pom.xml @@ -15,7 +15,7 @@ - org.jboss + io.smallrye jandex diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java index e8020ea6c1547..6afe4fb4d6e52 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java @@ -46,6 +46,7 @@ public class ClientBuilderImpl extends ClientBuilder { private char[] keystorePassword; private SSLContext sslContext; private KeyStore trustStore; + private char[] trustStorePassword; private String proxyHost; private int proxyPort; @@ -88,7 +89,12 @@ public ClientBuilder keyStore(KeyStore keyStore, char[] password) { @Override public ClientBuilder trustStore(KeyStore trustStore) { + return trustStore(trustStore, null); + } + + public ClientBuilder trustStore(KeyStore trustStore, char[] password) { this.trustStore = trustStore; + this.trustStorePassword = password; return this; } @@ -164,7 +170,7 @@ public ClientBuilder clientLogger(ClientLogger clientLogger) { @Override public ClientImpl build() { Buffer keyStore = asBuffer(this.keyStore, keystorePassword); - Buffer trustStore = asBuffer(this.trustStore, EMPTY_CHAR_ARARAY); + Buffer trustStore = asBuffer(this.trustStore, this.trustStorePassword); HttpClientOptions options = Optional.ofNullable(configuration.getFromContext(HttpClientOptions.class)) .orElseGet(HttpClientOptions::new); @@ -185,7 +191,7 @@ public ClientImpl build() { if (trustStore != null) { JksOptions jks = new JksOptions(); jks.setValue(trustStore); - jks.setPassword(""); + jks.setPassword(trustStorePassword == null ? "" : new String(trustStorePassword)); options.setTrustStoreOptions(jks); } } diff --git a/independent-projects/resteasy-reactive/common/processor/pom.xml b/independent-projects/resteasy-reactive/common/processor/pom.xml index cdad011e35ef8..69fed04ce862a 100644 --- a/independent-projects/resteasy-reactive/common/processor/pom.xml +++ b/independent-projects/resteasy-reactive/common/processor/pom.xml @@ -15,7 +15,7 @@ - org.jboss + io.smallrye jandex diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/AsmUtil.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/AsmUtil.java index c12c423c62737..cc3bc29b06939 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/AsmUtil.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/AsmUtil.java @@ -11,6 +11,7 @@ import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; import org.jboss.jandex.TypeVariable; +import org.jboss.jandex.TypeVariableReference; import org.jboss.jandex.UnresolvedTypeVariable; import org.jboss.jandex.WildcardType; import org.objectweb.asm.MethodVisitor; @@ -216,6 +217,17 @@ private static void toSignature(StringBuilder sb, Type type, Function getAllKnownSubclasses(DotName className) { return allKnown; } + private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { + final Set subClassesToProcess = new HashSet(); + subClassesToProcess.add(className); + while (!subClassesToProcess.isEmpty()) { + final Iterator toProcess = subClassesToProcess.iterator(); + DotName name = toProcess.next(); + toProcess.remove(); + processedClasses.add(name); + getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + } + } + + private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, + Set processedClasses) { + final Collection directSubclasses = getKnownDirectSubclasses(name); + if (directSubclasses != null) { + for (final ClassInfo clazz : directSubclasses) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + allKnown.add(clazz); + subClassesToProcess.add(className); + } + } + } + } + + @Override + public Collection getKnownDirectSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getKnownDirectSubinterfaces(interfaceName); + } + Set directSubinterfaces = new HashSet<>(index.getKnownDirectSubinterfaces(interfaceName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isPresent() && additional.get().interfaceNames().contains(interfaceName)) { + directSubinterfaces.add(additional.get()); + } + } + return directSubinterfaces; + } + + @Override + public Collection getAllKnownSubinterfaces(DotName interfaceName) { + if (additionalClasses.isEmpty()) { + return index.getAllKnownSubinterfaces(interfaceName); + } + + Set result = new HashSet<>(); + + Queue workQueue = new ArrayDeque<>(); + Set alreadyProcessed = new HashSet<>(); + + workQueue.add(interfaceName); + while (!workQueue.isEmpty()) { + DotName iface = workQueue.remove(); + if (!alreadyProcessed.add(iface)) { + continue; + } + + for (ClassInfo directSubinterface : getKnownDirectSubinterfaces(iface)) { + result.add(directSubinterface); + workQueue.add(directSubinterface.name()); + } + } + + return result; + } + @Override public Collection getKnownDirectImplementors(DotName className) { if (additionalClasses.isEmpty()) { @@ -124,6 +194,27 @@ public Collection getAllKnownImplementors(DotName interfaceName) { return allKnown; } + private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, + Set processedClasses) { + final Collection list = getKnownDirectImplementors(name); + if (list != null) { + for (final ClassInfo clazz : list) { + final DotName className = clazz.name(); + if (!processedClasses.contains(className)) { + if (Modifier.isInterface(clazz.flags())) { + subInterfacesToProcess.add(className); + } else { + if (!allKnown.contains(clazz)) { + allKnown.add(clazz); + processedClasses.add(className); + getAllKnownSubClasses(className, allKnown, processedClasses); + } + } + } + } + } + } + @Override public Collection getAnnotations(DotName annotationName) { return index.getAnnotations(annotationName); @@ -149,51 +240,44 @@ public Collection getKnownUsers(DotName className) { return this.index.getKnownUsers(className); } - private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { - final Set subClassesToProcess = new HashSet(); - subClassesToProcess.add(className); - while (!subClassesToProcess.isEmpty()) { - final Iterator toProcess = subClassesToProcess.iterator(); - DotName name = toProcess.next(); - toProcess.remove(); - processedClasses.add(name); - getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); + @Override + public Collection getClassesInPackage(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getClassesInPackage(packageName); } - } - - private void getAllKnownSubClasses(DotName name, Set allKnown, Set subClassesToProcess, - Set processedClasses) { - final Collection directSubclasses = getKnownDirectSubclasses(name); - if (directSubclasses != null) { - for (final ClassInfo clazz : directSubclasses) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - allKnown.add(clazz); - subClassesToProcess.add(className); - } + Set classesInPackage = new HashSet<>(index.getClassesInPackage(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + if (Objects.equals(packageName, additional.get().name().packagePrefixName())) { + classesInPackage.add(additional.get()); } } + return classesInPackage; } - private void getKnownImplementors(DotName name, Set allKnown, Set subInterfacesToProcess, - Set processedClasses) { - final Collection list = getKnownDirectImplementors(name); - if (list != null) { - for (final ClassInfo clazz : list) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - if (Modifier.isInterface(clazz.flags())) { - subInterfacesToProcess.add(className); - } else { - if (!allKnown.contains(clazz)) { - allKnown.add(clazz); - processedClasses.add(className); - getAllKnownSubClasses(className, allKnown, processedClasses); - } - } + @Override + public Set getSubpackages(DotName packageName) { + if (additionalClasses.isEmpty()) { + return index.getSubpackages(packageName); + } + Set subpackages = new HashSet<>(index.getSubpackages(packageName)); + for (Optional additional : additionalClasses.values()) { + if (additional.isEmpty()) { + continue; + } + DotName pkg = additional.get().name().packagePrefixName(); + while (pkg != null) { + DotName superPkg = pkg.packagePrefixName(); + if (superPkg != null && superPkg.equals(packageName)) { + subpackages.add(pkg); } + pkg = superPkg; } + } + return subpackages; } private Optional computeAdditional(DotName className) { diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 690c7443b5aef..513c58149396e 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -41,14 +41,14 @@ 11 2.0.2 - 2.4.3.Final + 3.0.0 1.12.12 5.9.0 3.8.6 3.23.1 3.5.0.Final 1.3.5 - 1.1.0.Final + 1.2.0.Final 2.2.3 3.0.0-M7 @@ -154,7 +154,7 @@ - org.jboss + io.smallrye jandex ${jandex.version} diff --git a/independent-projects/resteasy-reactive/server/jsonb/pom.xml b/independent-projects/resteasy-reactive/server/jsonb/pom.xml index 5213908b714b5..61859d2038764 100644 --- a/independent-projects/resteasy-reactive/server/jsonb/pom.xml +++ b/independent-projects/resteasy-reactive/server/jsonb/pom.xml @@ -111,6 +111,7 @@ io.quarkus.jakarta-jaxb-switch + io.quarkus.jakarta-json-cleanup diff --git a/independent-projects/resteasy-reactive/server/processor/pom.xml b/independent-projects/resteasy-reactive/server/processor/pom.xml index 16d112c07e8c1..c4623210140c9 100644 --- a/independent-projects/resteasy-reactive/server/processor/pom.xml +++ b/independent-projects/resteasy-reactive/server/processor/pom.xml @@ -15,7 +15,7 @@ - org.jboss + io.smallrye jandex diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index ecbf1f2673203..84fd8512e6d30 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -62,7 +62,7 @@ 20 2.11.0 1.13.1 - 1.2.3 + 3.0.0 registry-client diff --git a/independent-projects/tools/registry-client/pom.xml b/independent-projects/tools/registry-client/pom.xml index f3efab5cb315e..84c6a71a14aa8 100644 --- a/independent-projects/tools/registry-client/pom.xml +++ b/independent-projects/tools/registry-client/pom.xml @@ -44,9 +44,9 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin - ${jandex-maven-plugin.version} + ${jandex.version} make-index diff --git a/integration-tests/common-jpa-entities/pom.xml b/integration-tests/common-jpa-entities/pom.xml index d5ee8734e20b1..b029b6d3d3ddc 100644 --- a/integration-tests/common-jpa-entities/pom.xml +++ b/integration-tests/common-jpa-entities/pom.xml @@ -24,7 +24,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/integration-tests/gradle/src/main/resources/jandex-basic-multi-module-project/common/build.gradle b/integration-tests/gradle/src/main/resources/jandex-basic-multi-module-project/common/build.gradle index 19a292e288dec..02bd53a2ac5f2 100644 --- a/integration-tests/gradle/src/main/resources/jandex-basic-multi-module-project/common/build.gradle +++ b/integration-tests/gradle/src/main/resources/jandex-basic-multi-module-project/common/build.gradle @@ -1,9 +1,13 @@ plugins { id 'io.quarkus' id 'java-library' - id "org.kordamp.gradle.jandex" version "0.13.0" + id "org.kordamp.gradle.jandex" version "1.0.0" } dependencies { -} \ No newline at end of file +} + +jandex { + version = "3.0.0" +} diff --git a/integration-tests/jpa-mapping-xml/modern-library-a/pom.xml b/integration-tests/jpa-mapping-xml/modern-library-a/pom.xml index 4ae2ab65279cb..971f7fb8c8d10 100644 --- a/integration-tests/jpa-mapping-xml/modern-library-a/pom.xml +++ b/integration-tests/jpa-mapping-xml/modern-library-a/pom.xml @@ -44,7 +44,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/integration-tests/jpa-mapping-xml/modern-library-b/pom.xml b/integration-tests/jpa-mapping-xml/modern-library-b/pom.xml index d231faaffbe7d..5ccb00936280c 100644 --- a/integration-tests/jpa-mapping-xml/modern-library-b/pom.xml +++ b/integration-tests/jpa-mapping-xml/modern-library-b/pom.xml @@ -44,7 +44,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/integration-tests/keycloak-authorization/pom.xml b/integration-tests/keycloak-authorization/pom.xml index 89aa96d57ce33..c2ad8eeac7c0a 100644 --- a/integration-tests/keycloak-authorization/pom.xml +++ b/integration-tests/keycloak-authorization/pom.xml @@ -51,24 +51,6 @@ rest-assured test - - org.keycloak - keycloak-admin-client - - - jakarta.activation - jakarta.activation-api - - - org.jboss.spec.javax.ws.rs - jboss-jaxrs-api_3.0_spec - - - com.sun.activation - jakarta.activation - - - diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/filter/TestFilter.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/filter/TestFilter.java index 30163df431240..964a97ad1850e 100644 --- a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/filter/TestFilter.java +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/filter/TestFilter.java @@ -3,13 +3,21 @@ import java.util.logging.Filter; import java.util.logging.LogRecord; +import org.eclipse.microprofile.config.inject.ConfigProperty; + import io.quarkus.logging.LoggingFilter; @LoggingFilter(name = "my-filter") -public final class TestFilter implements Filter { +public class TestFilter implements Filter { + + private final String part; + + public TestFilter(@ConfigProperty(name = "my-filter.part") String part) { + this.part = part; + } @Override public boolean isLoggable(LogRecord record) { - return !record.getMessage().contains("TEST"); + return !record.getMessage().contains(part); } } diff --git a/integration-tests/logging-min-level-set/src/main/resources/application.properties b/integration-tests/logging-min-level-set/src/main/resources/application.properties index e33158f666c73..89dddefeda7c1 100644 --- a/integration-tests/logging-min-level-set/src/main/resources/application.properties +++ b/integration-tests/logging-min-level-set/src/main/resources/application.properties @@ -4,3 +4,4 @@ quarkus.log.category."io.quarkus.it.logging.minlevel.set.below".min-level=TRACE quarkus.log.category."io.quarkus.it.logging.minlevel.set.below.child".min-level=inherit quarkus.log.category."io.quarkus.it.logging.minlevel.set.promote".min-level=ERROR quarkus.log.console.filter=my-filter +my-filter.part=TEST diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java index 9f4941b88e385..fbe80108b4407 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java @@ -174,12 +174,6 @@ public String getNameCallbackJwtNotUsedAfterRedirect() { throw new InternalServerErrorException("This method must not be invoked"); } - @GET - @Path("tenant-logout") - public String getTenantLogout() { - return "Tenant Logout"; - } - @GET @Path("access") public String getAccessToken() { diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantAutoRefresh.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantAutoRefresh.java index 9d5e9b4345070..092d6c8b63f87 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantAutoRefresh.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantAutoRefresh.java @@ -1,15 +1,20 @@ package io.quarkus.it.keycloak; +import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import io.quarkus.security.Authenticated; +import io.vertx.ext.web.RoutingContext; @Path("/tenant-autorefresh") public class TenantAutoRefresh { + @Inject + RoutingContext context; + @Authenticated @GET public String getTenantLogout() { - return "Tenant AutoRefresh"; + return "Tenant AutoRefresh, refreshed: " + (context.get("refresh_token_grant_response") != null); } } diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantLogout.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantLogout.java index cd9568ed14035..5ffabd9a6b9ea 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantLogout.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantLogout.java @@ -1,5 +1,6 @@ package io.quarkus.it.keycloak; +import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.Path; @@ -9,6 +10,7 @@ import javax.ws.rs.core.HttpHeaders; import io.quarkus.security.Authenticated; +import io.vertx.ext.web.RoutingContext; @Path("/tenant-logout") public class TenantLogout { @@ -16,10 +18,13 @@ public class TenantLogout { @Context HttpHeaders headers; + @Inject + RoutingContext context; + @Authenticated @GET public String getTenantLogout() { - return "Tenant Logout"; + return "Tenant Logout, refreshed: " + (context.get("refresh_token_grant_response") != null); } // It is needed for the proactive-auth=false to work: /tenant-logout/logout should match a user initiated logout request diff --git a/integration-tests/oidc-code-flow/src/main/resources/application.properties b/integration-tests/oidc-code-flow/src/main/resources/application.properties index c693d6853e3d7..69b81d6eccce7 100644 --- a/integration-tests/oidc-code-flow/src/main/resources/application.properties +++ b/integration-tests/oidc-code-flow/src/main/resources/application.properties @@ -86,6 +86,7 @@ quarkus.oidc.tenant-autorefresh.application-type=web-app quarkus.oidc.tenant-autorefresh.authentication.cookie-path=/tenant-autorefresh quarkus.oidc.tenant-autorefresh.token.refresh-expired=true quarkus.oidc.tenant-autorefresh.token.refresh-token-time-skew=30S +quarkus.oidc.tenant-autorefresh.authentication.remove-redirect-parameters=false # Tenant which is used to test that the redirect_uri https scheme is enforced. quarkus.oidc.tenant-https.auth-server-url=${keycloak.url}/realms/quarkus diff --git a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java index c1571712c6937..a574802dbb855 100644 --- a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java +++ b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java @@ -413,7 +413,7 @@ public void testRPInitiatedLogout() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertTrue(page.asText().contains("Tenant Logout")); + assertEquals("Tenant Logout, refreshed: false", page.asText()); assertNotNull(getSessionCookie(webClient, "tenant-logout")); page = webClient.getPage("http://localhost:8081/tenant-logout/logout"); @@ -422,11 +422,20 @@ public void testRPInitiatedLogout() throws IOException { page = webClient.getPage("http://localhost:8081/tenant-logout"); assertEquals("Sign in to logout-realm", page.getTitleText()); - loginForm = page.getForms().get(0); + webClient.getCookieManager().clearCookies(); + } + } + + @Test + public void testTokenRefresh() throws IOException { + try (final WebClient webClient = createWebClient()) { + HtmlPage page = webClient.getPage("http://localhost:8081/tenant-logout"); + assertEquals("Sign in to logout-realm", page.getTitleText()); + HtmlForm loginForm = page.getForms().get(0); loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertTrue(page.asText().contains("Tenant Logout")); + assertEquals("Tenant Logout, refreshed: false", page.asText()); Cookie sessionCookie = getSessionCookie(webClient, "tenant-logout"); assertNotNull(sessionCookie); @@ -445,13 +454,14 @@ public Boolean call() throws Exception { // Should not redirect to OP but silently refresh token Cookie newSessionCookie = getSessionCookie(webClient, "tenant-logout"); assertNotNull(newSessionCookie); - return !idToken.equals(getIdToken(newSessionCookie)); + return webResponse.getContentAsString().equals("Tenant Logout, refreshed: true") + && !idToken.equals(getIdToken(newSessionCookie)); } }); // local session refreshed and still valid page = webClient.getPage("http://localhost:8081/tenant-logout"); - assertTrue(page.asText().contains("Tenant Logout")); + assertEquals("Tenant Logout, refreshed: false", page.asText()); assertNotNull(getSessionCookie(webClient, "tenant-logout")); //wait now so that we reach the refresh timeout @@ -495,7 +505,7 @@ public void testTokenAutoRefresh() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertTrue(page.asText().contains("Tenant AutoRefresh")); + assertEquals("Tenant AutoRefresh, refreshed: false", page.asText()); Cookie sessionCookie = getSessionCookie(webClient, "tenant-autorefresh"); assertNotNull(sessionCookie); @@ -504,24 +514,11 @@ public void testTokenAutoRefresh() throws IOException { // Auto-refresh-interval is 30 secs so every call auto-refreshes the token // Right now the original ID token is still valid but will be auto-refreshed page = webClient.getPage("http://localhost:8081/tenant-autorefresh"); - assertTrue(page.getBody().asText().contains("Tenant AutoRefresh")); + assertEquals("Tenant AutoRefresh, refreshed: true", page.asText()); sessionCookie = getSessionCookie(webClient, "tenant-autorefresh"); assertNotNull(sessionCookie); String nextIdToken = getIdToken(sessionCookie); assertNotEquals(idToken, nextIdToken); - idToken = nextIdToken; - - //wait now so that we reach the ID token timeout - await().atLeast(6, TimeUnit.SECONDS); - - // ID token has expired, must be refreshed, auto-refresh-interval must not cause - // an auto-refresh loop even though it is larger than the ID token lifespan - page = webClient.getPage("http://localhost:8081/tenant-autorefresh"); - assertTrue(page.getBody().asText().contains("Tenant AutoRefresh")); - sessionCookie = getSessionCookie(webClient, "tenant-autorefresh"); - assertNotNull(sessionCookie); - nextIdToken = getIdToken(sessionCookie); - assertNotEquals(idToken, nextIdToken); webClient.getCookieManager().clearCookies(); } diff --git a/integration-tests/reactive-messaging-amqp/pom.xml b/integration-tests/reactive-messaging-amqp/pom.xml index da8bdcb487de4..2ccd4e67e7cf8 100644 --- a/integration-tests/reactive-messaging-amqp/pom.xml +++ b/integration-tests/reactive-messaging-amqp/pom.xml @@ -59,24 +59,6 @@ org.apache.activemq artemis-server test - - - org.jboss.logmanager - jboss-logmanager - - - commons-logging - commons-logging - - - org.apache.johnzon - johnzon-core - - - jakarta.json - jakarta.json-api - - org.apache.activemq diff --git a/integration-tests/rest-client-reactive/pom.xml b/integration-tests/rest-client-reactive/pom.xml index 8889bbc39f7ad..d81583d01c89e 100644 --- a/integration-tests/rest-client-reactive/pom.xml +++ b/integration-tests/rest-client-reactive/pom.xml @@ -11,6 +11,11 @@ quarkus-integration-test-rest-client-reactive Quarkus - Integration Tests - REST Client Reactive + + ${project.build.directory}/self-signed.p12 + changeit + + @@ -165,6 +170,31 @@ + + + uk.co.automatictester + truststore-maven-plugin + ${truststore-maven-plugin.version} + + + self-signed-truststore + generate-test-resources + + generate-truststore + + + PKCS12 + ${self-signed.trust-store} + ${self-signed.trust-store-password} + + self-signed.badssl.com:443 + + true + LEAF + + + + diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java index 5b6252ce83a00..1e311fef17815 100644 --- a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java @@ -21,6 +21,7 @@ import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.quarkus.it.rest.client.main.MyResponseExceptionMapper.MyException; +import io.quarkus.it.rest.client.main.selfsigned.ExternalSelfSignedClient; import io.smallrye.mutiny.Uni; import io.vertx.core.Future; import io.vertx.core.json.Json; @@ -44,6 +45,9 @@ public class ClientCallingResource { @RestClient FaultToleranceOnInterfaceClient faultToleranceOnInterfaceClient; + @RestClient + ExternalSelfSignedClient externalSelfSignedClient; + @Inject InMemorySpanExporter inMemorySpanExporter; @@ -165,6 +169,9 @@ void init(@Observes Router router) { }); router.get("/with%20space").handler(rc -> rc.response().setStatusCode(200).end()); + + router.get("/self-signed").blockingHandler( + rc -> rc.response().setStatusCode(200).end(String.valueOf(externalSelfSignedClient.invoke().getStatus()))); } private Future success(RoutingContext rc, String body) { diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java new file mode 100644 index 0000000000000..7d2610f75b4be --- /dev/null +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java @@ -0,0 +1,15 @@ +package io.quarkus.it.rest.client.main.selfsigned; + +import javax.ws.rs.GET; +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.faulttolerance.Retry; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient(baseUri = "https://self-signed.badssl.com/", configKey = "self-signed") +public interface ExternalSelfSignedClient { + + @GET + @Retry(delay = 1000) + Response invoke(); +} diff --git a/integration-tests/rest-client-reactive/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/main/resources/application.properties index 392722ce0c912..03c013f85ce30 100644 --- a/integration-tests/rest-client-reactive/src/main/resources/application.properties +++ b/integration-tests/rest-client-reactive/src/main/resources/application.properties @@ -1,4 +1,7 @@ w-exception-mapper/mp-rest/url=${test.url} w-fault-tolerance/mp-rest/url=${test.url} io.quarkus.it.rest.client.main.ParamClient/mp-rest/url=${test.url} -io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url} \ No newline at end of file +io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url} +# HTTPS +quarkus.rest-client.self-signed.trust-store=${self-signed.trust-store} +quarkus.rest-client.self-signed.trust-store-password=${self-signed.trust-store-password} diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java new file mode 100644 index 0000000000000..1348f532290fc --- /dev/null +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java @@ -0,0 +1,21 @@ +package io.quarkus.it.rest.client.selfsigned; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ExternalSelfSignedTestCase { + + @Test + public void should_accept_self_signed_certs() { + when() + .get("/self-signed") + .then() + .statusCode(200) + .body(is("200")); + } +} diff --git a/jakarta/rewrite.yml b/jakarta/rewrite.yml index 8b37a399dfdc6..2afe3e889c567 100644 --- a/jakarta/rewrite.yml +++ b/jakarta/rewrite.yml @@ -183,7 +183,7 @@ recipeList: # WildFly Security - org.openrewrite.maven.ChangePropertyValue: key: wildfly-elytron.version - newValue: 2.0.0.Beta3 + newValue: 2.0.0.Final --- type: specs.openrewrite.org/v1beta/recipe name: io.quarkus.jakarta-jaxrs-jaxb @@ -316,6 +316,31 @@ recipeList: artifactId: resteasy-json-p-provider exclusionGroupId: jakarta.json exclusionArtifactId: jakarta.json-api + - org.openrewrite.maven.RemoveExclusion: + groupId: org.apache.activemq + artifactId: artemis-server + exclusionGroupId: jakarta.json + exclusionArtifactId: jakarta.json-api + - org.openrewrite.maven.RemoveExclusion: + groupId: org.apache.activemq + artifactId: artemis-amqp-protocol + exclusionGroupId: jakarta.json + exclusionArtifactId: jakarta.json-api + - org.openrewrite.maven.RemoveExclusion: + groupId: org.eclipse + artifactId: yasson + exclusionGroupId: jakarta.json + exclusionArtifactId: jakarta.json-api + - org.openrewrite.maven.RemoveExclusion: + groupId: org.eclipse + artifactId: yasson + exclusionGroupId: org.glassfish + exclusionArtifactId: jakarta.json + - org.openrewrite.maven.RemoveExclusion: + groupId: jakarta.json.bind + artifactId: jakarta.json.bind-api + exclusionGroupId: jakarta.json + exclusionArtifactId: jakarta.json-api --- type: specs.openrewrite.org/v1beta/recipe name: io.quarkus.jakarta-json @@ -544,7 +569,7 @@ recipeList: newValue: 6.0.0-RC4 - org.openrewrite.maven.ChangePropertyValue: key: smallrye-graphql.version - newValue: 2.0.0.RC8 + newValue: 2.0.0.RC9 - org.openrewrite.maven.ChangePropertyValue: key: smallrye-health.version newValue: 4.0.0 @@ -582,10 +607,10 @@ recipeList: newValue: 4.0.1 - org.openrewrite.maven.ChangePropertyValue: key: smallrye-metrics.version - newValue: 4.0.0-RC1 + newValue: 4.0.0 - org.openrewrite.maven.ChangePropertyValue: key: smallrye-open-api.version - newValue: 3.0.0-RC4 + newValue: 3.0.0 - org.openrewrite.maven.ChangePropertyValue: key: microprofile-opentracing-api.version newValue: 3.0 @@ -652,7 +677,7 @@ recipeList: newValue: 3.0 - org.openrewrite.maven.ChangePropertyValue: key: resteasy-reactive-testsuite.repo.ref - newValue: c5785f3465fa87395574fde1274c1712b3aa728b + newValue: 4116f1a0c5605ad00d7779367dac8002af8c6882 --- type: specs.openrewrite.org/v1beta/recipe name: io.quarkus.maven.javax.managed diff --git a/jakarta/transform.sh b/jakarta/transform.sh index cfad1b3fbd6bc..b19632b0c6625 100755 --- a/jakarta/transform.sh +++ b/jakarta/transform.sh @@ -230,8 +230,9 @@ sed -i 's@com.sun.xml.bind.v2.ContextFactory@org.glassfish.jaxb.runtime.v2.Conte sed -i '/com.sun.xml.internal.bind.v2.ContextFactory/d' extensions/jaxb/deployment/src/main/java/io/quarkus/jaxb/deployment/JaxbProcessor.java ## JSON-P implementation switch -sed -i 's@org.glassfish:jakarta.json@org.eclipse.parsson:jakarta.json@g' extensions/logging-json/runtime/pom.xml -sed -i 's@org.glassfish:jakarta.json@org.eclipse.parsson:jakarta.json@g' extensions/jsonp/runtime/pom.xml +sed -i 's@org.glassfish:jakarta.json@org.eclipse.parsson:parsson\n jakarta.json:jakarta.json-api@g' extensions/logging-json/runtime/pom.xml +sed -i 's@org.glassfish:jakarta.json@org.eclipse.parsson:parsson@g' extensions/jsonp/runtime/pom.xml +sed -i 's@org.glassfish:javax.json@org.glassfish:javax.json\n org.glassfish:jakarta.json\n org.eclipse.parsson:jakarta.json@g' extensions/jsonp/runtime/pom.xml sed -i 's@import org.glassfish.json.JsonProviderImpl;@import org.eclipse.parsson.JsonProviderImpl;@g' extensions/jsonp/deployment/src/main/java/io/quarkus/jsonp/deployment/JsonpProcessor.java ## cleanup phase - needs to be done once everything has been rewritten @@ -299,6 +300,9 @@ sed -i 's@org.jboss.narayana.rts:narayana-lra@org.jboss.narayana.rts:narayana-lr sed -i 's@org.jboss.narayana.rts:lra-client@org.jboss.narayana.rts:lra-client-jakarta@g' extensions/narayana-lra/runtime/pom.xml sed -i 's@META-INF/services/javax.ws.rs.client.ClientBuilder@META-INF/services/jakarta.ws.rs.client.ClientBuilder@g' extensions/narayana-lra/runtime/pom.xml +find integration-tests/gradle -name build.gradle | xargs sed -i 's/javax.enterprise.context.ApplicationScoped/jakarta.enterprise.context.ApplicationScoped/g' +find integration-tests/gradle -name build.gradle | xargs sed -i 's/javax.ws.rs.Path/jakarta.ws.rs.Path/g' + transform_documentation sed -i 's@javax/ws/rs@jakarta/ws/rs@g' docs/src/main/asciidoc/resteasy-reactive.adoc sed -i 's@https://javadoc.io/doc/jakarta.ws.rs/jakarta.ws.rs-api/2.1.1@https://javadoc.io/doc/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0@g' docs/src/main/asciidoc/resteasy-reactive.adoc diff --git a/tcks/microprofile-fault-tolerance/pom.xml b/tcks/microprofile-fault-tolerance/pom.xml index 6236ed6dd6cad..0035bad6ad1f6 100644 --- a/tcks/microprofile-fault-tolerance/pom.xml +++ b/tcks/microprofile-fault-tolerance/pom.xml @@ -37,9 +37,6 @@ org.eclipse.microprofile.fault.tolerance.tck.interceptor.xmlInterceptorEnabling.FaultToleranceInterceptorEnableByXmlTest - - org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureRatioNegTest - org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureRatioPosTest diff --git a/tcks/microprofile-rest-client-reactive/pom.xml b/tcks/microprofile-rest-client-reactive/pom.xml index f1892acafbac3..78639150f1b62 100644 --- a/tcks/microprofile-rest-client-reactive/pom.xml +++ b/tcks/microprofile-rest-client-reactive/pom.xml @@ -101,9 +101,8 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin - ${jandex-maven-plugin.version} make-index diff --git a/test-framework/common/pom.xml b/test-framework/common/pom.xml index 9f4a830fe20d6..7ace31a5d36a4 100644 --- a/test-framework/common/pom.xml +++ b/test-framework/common/pom.xml @@ -27,7 +27,7 @@ quarkus-bootstrap-gradle-resolver - org.jboss + io.smallrye jandex diff --git a/test-framework/security-jwt/pom.xml b/test-framework/security-jwt/pom.xml index 8bd1a0123be40..0aa072bb0146d 100644 --- a/test-framework/security-jwt/pom.xml +++ b/test-framework/security-jwt/pom.xml @@ -38,7 +38,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/test-framework/security-oidc/pom.xml b/test-framework/security-oidc/pom.xml index 095270779e35a..27aba21bfe3f4 100644 --- a/test-framework/security-oidc/pom.xml +++ b/test-framework/security-oidc/pom.xml @@ -38,7 +38,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/test-framework/security-webauthn/pom.xml b/test-framework/security-webauthn/pom.xml index 807d734ea3b95..38923a799df21 100644 --- a/test-framework/security-webauthn/pom.xml +++ b/test-framework/security-webauthn/pom.xml @@ -48,7 +48,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin diff --git a/test-framework/security/pom.xml b/test-framework/security/pom.xml index 56d34268f01da..56420535a7f2a 100644 --- a/test-framework/security/pom.xml +++ b/test-framework/security/pom.xml @@ -38,7 +38,7 @@ - org.jboss.jandex + io.smallrye jandex-maven-plugin