diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml
index 130660b300b42..495652aacd6a4 100644
--- a/.github/workflows/ci-actions-incremental.yml
+++ b/.github/workflows/ci-actions-incremental.yml
@@ -178,9 +178,10 @@ jobs:
uses: actions/upload-artifact@v2
if: ${{ failure() || cancelled() }}
with:
- name: "build-reports-JVM Tests - JDK ${{matrix.java.name}}"
+ name: "build-reports-Initial JDK 11 Build"
path: |
target/build-report.json
+ LICENSE.txt
retention-days: 2
calculate-test-jobs:
@@ -322,6 +323,7 @@ jobs:
path: |
**/target/*-reports/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
- name: Upload gc.log
uses: actions/upload-artifact@v2
@@ -388,6 +390,7 @@ jobs:
path: |
**/target/*-reports/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
gradle-tests:
@@ -447,6 +450,7 @@ jobs:
**/build/test-results/test/TEST-*.xml
**/target/*-reports/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
devtools-tests:
@@ -506,6 +510,7 @@ jobs:
path: |
**/target/*-reports/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
tcks-test:
@@ -564,6 +569,7 @@ jobs:
path: |
**/target/*-reports/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
native-tests:
@@ -643,4 +649,5 @@ jobs:
**/target/*-reports/TEST-*.xml
**/build/test-results/test/TEST-*.xml
target/build-report.json
+ LICENSE.txt
retention-days: 2
diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index c101beff0e49f..fdaef17a893fb 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -47,14 +47,14 @@
3.1.13.0.12.1.10
- 1.3.1
+ 1.3.22.0.15.2.13.2.11.2.01.0.132.6.0
- 2.12.0
+ 2.13.03.9.11.2.11.3.5
@@ -110,7 +110,7 @@
1.16.1.Final1.8.7.Final3.4.2.Final
- 4.1.2
+ 4.1.34.5.134.4.144.1.4
@@ -121,7 +121,7 @@
2.7.48.0.267.2.2.jre8
- 21.1.0.0
+ 21.3.0.010.14.2.011.5.6.01.2.6
@@ -132,7 +132,7 @@
12.1.7.Final4.4.1.Final2.9.2
- 4.1.65.Final
+ 4.1.67.Final1.0.33.4.2.Final1.0.0
@@ -170,14 +170,14 @@
3.14.95.0.10.1.0
- 5.7.0
+ 5.7.22.2.05.2.SP42.1.SP25.2.Final2.1.SP13.11.2
- 5.3.1
+ 5.8.04.81.1.4.Final14.0.0
@@ -353,6 +353,15 @@
import
+
+
+ com.oracle.database.jdbc
+ ojdbc-bom
+ ${oracle-jdbc.version}
+ pom
+ import
+
+
io.smallrye.reactive
@@ -1608,6 +1617,11 @@
quarkus-mailer${project.version}
+
+ io.quarkus
+ quarkus-mailer-deployment
+ ${project.version}
+ io.quarkusquarkus-mongodb-client
@@ -4489,11 +4503,6 @@
mssql-jdbc${mssql-jdbc.version}
-
- com.oracle.database.jdbc
- ojdbc8
- ${oracle-jdbc.version}
- org.elasticsearch.clientelasticsearch-rest-client
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java
new file mode 100644
index 0000000000000..8c2cf955c2639
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java
@@ -0,0 +1,41 @@
+package io.quarkus.deployment.builditem.nativeimage;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * A build item that allows extension to configure the native-image compiler to effectively
+ * ignore certain configuration files in specific jars.
+ *
+ * The {@code jarFile} property specifies the name of the jar file or a regular expression that can be used to
+ * match multiple jar files.
+ * Matching jar files using regular expressions should be done as a last resort.
+ *
+ * The {@code resourceName} property specifies the name of the resource file or a regular expression that can be used to
+ * match multiple resource files.
+ * For the match to work, the resources need to be part of the matched jar file(s) (see {@code jarFile}).
+ * Matching resource files using regular expressions should be done as a last resort.
+ *
+ * See https://github.com/oracle/graal/pull/3179 for more details.
+ */
+public final class ExcludeConfigBuildItem extends MultiBuildItem {
+
+ private final String jarFile;
+ private final String resourceName;
+
+ public ExcludeConfigBuildItem(String jarFile, String resourceName) {
+ this.jarFile = jarFile;
+ this.resourceName = resourceName;
+ }
+
+ public ExcludeConfigBuildItem(String jarFile) {
+ this(jarFile, "/META-INF/native-image/native-image\\.properties");
+ }
+
+ public String getJarFile() {
+ return jarFile;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathAggregateBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathAggregateBuildItem.java
new file mode 100644
index 0000000000000..147d85716e390
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathAggregateBuildItem.java
@@ -0,0 +1,21 @@
+package io.quarkus.deployment.builditem.nativeimage;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+
+/**
+ * Do not use directly: use {@see io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathBuildItem}
+ * instead.
+ */
+public final class NativeImageAllowIncompleteClasspathAggregateBuildItem extends SimpleBuildItem {
+
+ private final boolean allow;
+
+ public NativeImageAllowIncompleteClasspathAggregateBuildItem(boolean allow) {
+ this.allow = allow;
+ }
+
+ public boolean isAllow() {
+ return allow;
+ }
+
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathBuildItem.java
new file mode 100644
index 0000000000000..c0d6223607f54
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageAllowIncompleteClasspathBuildItem.java
@@ -0,0 +1,32 @@
+package io.quarkus.deployment.builditem.nativeimage;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * If any build item of this type is produced, the native-image build tool
+ * will run with {@literal --allow-incomplete-classpath} set.
+ *
+ * This should be strongly discouraged as it makes diagnostics of any issue
+ * much more complex, and we have it seen affect error message of code
+ * seemingly unrelated to the code which is having the broken classpath.
+ *
+ * Use of this build item will trigger a warning during build.
+ *
+ * @Deprecated Please don't use it unless there is general consensus that we can't practically find a better solution.
+ */
+@Deprecated
+public final class NativeImageAllowIncompleteClasspathBuildItem extends MultiBuildItem {
+
+ private final String extensionName;
+
+ /**
+ * @param extensionName Name the extension requiring this, so that it can be shamed appropriately during build.
+ */
+ public NativeImageAllowIncompleteClasspathBuildItem(String extensionName) {
+ this.extensionName = extensionName;
+ }
+
+ public String getExtensionName() {
+ return extensionName;
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java
deleted file mode 100644
index 2cbe8dc5f7340..0000000000000
--- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package io.quarkus.deployment.configuration;
-
-import static io.quarkus.deployment.builditem.ConfigClassBuildItem.Type.MAPPING;
-import static io.quarkus.deployment.builditem.ConfigClassBuildItem.Type.PROPERTIES;
-import static java.util.Collections.emptySet;
-import static org.eclipse.microprofile.config.inject.ConfigProperties.UNCONFIGURED_PREFIX;
-import static org.jboss.jandex.AnnotationTarget.Kind.CLASS;
-import static org.jboss.jandex.AnnotationTarget.Kind.FIELD;
-import static org.jboss.jandex.AnnotationTarget.Kind.METHOD_PARAMETER;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-import org.jboss.jandex.AnnotationInstance;
-import org.jboss.jandex.AnnotationTarget;
-import org.jboss.jandex.AnnotationValue;
-import org.jboss.jandex.ClassInfo;
-import org.jboss.jandex.ClassType;
-import org.jboss.jandex.DotName;
-import org.jboss.jandex.MethodInfo;
-
-import io.quarkus.deployment.annotations.BuildProducer;
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
-import io.quarkus.deployment.builditem.ConfigClassBuildItem;
-import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
-import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
-import io.smallrye.config.ConfigMapping;
-import io.smallrye.config.ConfigMappingLoader;
-import io.smallrye.config.ConfigMappingMetadata;
-
-public class ConfigMappingUtils {
- public static final DotName CONFIG_MAPPING_NAME = DotName.createSimple(ConfigMapping.class.getName());
-
- private ConfigMappingUtils() {
- }
-
- @BuildStep
- public static void generateConfigClasses(
- CombinedIndexBuildItem combinedIndex,
- BuildProducer generatedClasses,
- BuildProducer reflectiveClasses,
- BuildProducer configClasses,
- DotName configAnnotation) {
-
- for (AnnotationInstance instance : combinedIndex.getIndex().getAnnotations(configAnnotation)) {
- AnnotationTarget target = instance.target();
- AnnotationValue annotationPrefix = instance.value("prefix");
-
- if (target.kind().equals(FIELD)) {
- if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
- configClasses.produce(
- toConfigClassBuildItem(instance, toClass(target.asField().type().name()),
- annotationPrefix.asString()));
- continue;
- }
- }
-
- if (target.kind().equals(METHOD_PARAMETER)) {
- if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
- ClassType classType = target.asMethodParameter().method().parameters()
- .get(target.asMethodParameter().position()).asClassType();
- configClasses
- .produce(toConfigClassBuildItem(instance, toClass(classType.name()), annotationPrefix.asString()));
- continue;
- }
- }
-
- if (!target.kind().equals(CLASS)) {
- continue;
- }
-
- Class> configClass = toClass(target.asClass().name());
- String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse("");
-
- List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass);
- Set generatedClassesNames = new HashSet<>();
- Set mappingsInfo = new HashSet<>();
- configMappingsMetadata.forEach(mappingMetadata -> {
- generatedClasses.produce(
- new GeneratedClassBuildItem(true, mappingMetadata.getClassName(), mappingMetadata.getClassBytes()));
- reflectiveClasses
- .produce(ReflectiveClassBuildItem.builder(mappingMetadata.getInterfaceType()).methods(true).build());
- reflectiveClasses
- .produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName()).constructors(true).build());
-
- for (Class> parent : getHierarchy(mappingMetadata.getInterfaceType())) {
- reflectiveClasses.produce(ReflectiveClassBuildItem.builder(parent).methods(true).build());
- }
-
- generatedClassesNames.add(mappingMetadata.getClassName());
-
- ClassInfo mappingInfo = combinedIndex.getIndex()
- .getClassByName(DotName.createSimple(mappingMetadata.getInterfaceType().getName()));
- if (mappingInfo != null) {
- mappingsInfo.add(mappingInfo);
- }
- });
-
- // For implicit converters
- for (ClassInfo classInfo : mappingsInfo) {
- for (MethodInfo method : classInfo.methods()) {
- reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, method.returnType().name().toString()));
- }
- }
-
- configClasses.produce(toConfigClassBuildItem(instance, configClass, generatedClassesNames, prefix));
- }
- }
-
- private static Class> toClass(DotName dotName) {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- try {
- return classLoader.loadClass(dotName.toString());
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException("The class (" + dotName.toString() + ") cannot be created during deployment.", e);
- }
- }
-
- private static ConfigClassBuildItem toConfigClassBuildItem(
- AnnotationInstance instance,
- Class> configClass,
- String prefix) {
- return toConfigClassBuildItem(instance, configClass, emptySet(), prefix);
- }
-
- private static ConfigClassBuildItem toConfigClassBuildItem(
- AnnotationInstance instance,
- Class> configClass,
- Set generatedClasses,
- String prefix) {
- if (instance.name().equals(CONFIG_MAPPING_NAME)) {
- return new ConfigClassBuildItem(configClass, generatedClasses, prefix, MAPPING);
- } else {
- return new ConfigClassBuildItem(configClass, generatedClasses, prefix, PROPERTIES);
- }
- }
-
- private static List> getHierarchy(Class> mapping) {
- List> interfaces = new ArrayList<>();
- for (Class> i : mapping.getInterfaces()) {
- interfaces.add(i);
- interfaces.addAll(getHierarchy(i));
- }
- return interfaces;
- }
-}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java
index 377bac70e18c2..ee5ebd04d064d 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java
@@ -70,7 +70,6 @@
import io.quarkus.runtime.configuration.RuntimeConfigSource;
import io.quarkus.runtime.configuration.RuntimeConfigSourceFactory;
import io.quarkus.runtime.configuration.RuntimeConfigSourceProvider;
-import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.Converters;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
@@ -175,8 +174,6 @@ public final class RunTimeConfigurationGenerator {
static final MethodDescriptor CU_ADD_SOURCE_FACTORY_PROVIDER = MethodDescriptor.ofMethod(ConfigUtils.class,
"addSourceFactoryProvider",
void.class, SmallRyeConfigBuilder.class, ConfigSourceFactoryProvider.class);
- static final MethodDescriptor CU_WITH_MAPPING = MethodDescriptor.ofMethod(ConfigUtils.class, "addMapping",
- void.class, SmallRyeConfigBuilder.class, String.class, String.class);
static final MethodDescriptor RCS_NEW = MethodDescriptor.ofConstructor(RuntimeConfigSource.class, String.class);
static final MethodDescriptor RCSP_NEW = MethodDescriptor.ofConstructor(RuntimeConfigSourceProvider.class, String.class);
@@ -301,7 +298,6 @@ public static final class GenerateOperation implements AutoCloseable {
final Set runtimeConfigSources;
final Set runtimeConfigSourceProviders;
final Set runtimeConfigSourceFactories;
- final Set configMappings;
/**
* Regular converters organized by type. Each converter is stored in a separate field. Some are used
* only at build time, some only at run time, and some at both times.
@@ -339,7 +335,6 @@ public static final class GenerateOperation implements AutoCloseable {
runtimeConfigSources = builder.getRuntimeConfigSources();
runtimeConfigSourceProviders = builder.getRuntimeConfigSourceProviders();
runtimeConfigSourceFactories = builder.getRuntimeConfigSourceFactories();
- configMappings = builder.getConfigMappings();
cc = ClassCreator.builder().classOutput(classOutput).className(CONFIG_CLASS_NAME).setFinal(true).build();
generateEmptyParsers(cc);
// not instantiable
@@ -428,11 +423,6 @@ public static final class GenerateOperation implements AutoCloseable {
clinit.invokeStaticMethod(CU_ADD_SOURCE_FACTORY_PROVIDER, buildTimeBuilder,
clinit.newInstance(RCSF_NEW, clinit.load(discoveredConfigSourceFactory)));
}
- // add mappings
- for (ConfigClassWithPrefix configMapping : configMappings) {
- clinit.invokeStaticMethod(CU_WITH_MAPPING, buildTimeBuilder,
- clinit.load(configMapping.getKlass().getName()), clinit.load(configMapping.getPrefix()));
- }
clinitConfig = clinit.checkCast(clinit.invokeVirtualMethod(SRCB_BUILD, buildTimeBuilder),
SmallRyeConfig.class);
@@ -676,12 +666,6 @@ public void run() {
readConfig.newInstance(RCSF_NEW, readConfig.load(discoveredConfigSourceFactory)));
}
- // add mappings
- for (ConfigClassWithPrefix configMapping : configMappings) {
- readConfig.invokeStaticMethod(CU_WITH_MAPPING, runTimeBuilder,
- readConfig.load(configMapping.getKlass().getName()), readConfig.load(configMapping.getPrefix()));
- }
-
ResultHandle bootstrapConfig = null;
if (bootstrapConfigSetupNeeded()) {
bootstrapConfig = readBootstrapConfig.invokeVirtualMethod(SRCB_BUILD, bootstrapBuilder);
@@ -1703,8 +1687,6 @@ public static final class Builder {
private Set runtimeConfigSourceProviders;
private Set runtimeConfigSourceFactories;
- private Set configMappings;
-
Builder() {
}
@@ -1816,15 +1798,6 @@ public Builder setRuntimeConfigSourceFactories(final Set runtimeConfigSo
return this;
}
- Set getConfigMappings() {
- return configMappings;
- }
-
- public Builder setConfigMappings(final Set configMappings) {
- this.configMappings = configMappings;
- return this;
- }
-
public GenerateOperation build() {
return new GenerateOperation(this);
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/JunitTestRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/JunitTestRunner.java
index f3ebf58184b3c..1c6f5009c660c 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/JunitTestRunner.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/JunitTestRunner.java
@@ -73,8 +73,10 @@
import io.quarkus.deployment.QuarkusClassWriter;
import io.quarkus.deployment.dev.ClassScanResult;
import io.quarkus.deployment.dev.DevModeContext;
+import io.quarkus.deployment.dev.RuntimeUpdatesProcessor;
import io.quarkus.deployment.util.IoUtil;
import io.quarkus.dev.console.QuarkusConsole;
+import io.quarkus.dev.testing.TestWatchedFiles;
import io.quarkus.dev.testing.TracingHandler;
/**
@@ -379,6 +381,11 @@ public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry e
QuarkusConsole.INSTANCE.setOutputFilter(null);
+ //this has to happen before notifying the listeners
+ Map watched = TestWatchedFiles.retrieveWatchedFilePaths();
+ if (watched != null) {
+ RuntimeUpdatesProcessor.INSTANCE.setWatchedFilePaths(watched, true);
+ }
for (TestRunListener listener : listeners) {
listener.runComplete(new TestRunResults(runId, classScanResult, classScanResult == null, start,
System.currentTimeMillis(), toResultsMap(testState.getCurrentResults())));
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java
index 3c057a5422295..cdbe69cf0fdd4 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java
@@ -24,8 +24,6 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.deployment.dev.ClassScanResult;
import io.quarkus.deployment.dev.DevModeContext;
-import io.quarkus.deployment.dev.RuntimeUpdatesProcessor;
-import io.quarkus.dev.testing.TestWatchedFiles;
import io.quarkus.runtime.configuration.HyphenateEnumConverter;
public class TestRunner {
@@ -254,10 +252,6 @@ public void noTests(TestRunResults results) {
synchronized (this) {
runner = null;
}
- Map watched = TestWatchedFiles.retrieveWatchedFilePaths();
- if (watched != null) {
- RuntimeUpdatesProcessor.INSTANCE.setWatchedFilePaths(watched, true);
- }
if (disabled) {
return;
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
index 2210a067887cb..75ba060b0e62c 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
@@ -26,6 +26,8 @@
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.util.IoUtils;
import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.nativeimage.ExcludeConfigBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathAggregateBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
import io.quarkus.deployment.pkg.NativeConfig;
@@ -81,7 +83,9 @@ ArtifactResultBuildItem nativeSourcesResult(NativeConfig nativeConfig,
NativeImageSourceJarBuildItem nativeImageSourceJarBuildItem,
OutputTargetBuildItem outputTargetBuildItem,
PackageConfig packageConfig,
- List nativeImageProperties) {
+ List nativeImageProperties,
+ List excludeConfigs,
+ NativeImageAllowIncompleteClasspathAggregateBuildItem incompleteClassPathAllowed) {
Path outputDir;
try {
@@ -100,6 +104,8 @@ ArtifactResultBuildItem nativeSourcesResult(NativeConfig nativeConfig,
.setNativeConfig(nativeConfig)
.setOutputTargetBuildItem(outputTargetBuildItem)
.setNativeImageProperties(nativeImageProperties)
+ .setBrokenClasspath(incompleteClassPathAllowed.isAllow())
+ .setExcludeConfigs(excludeConfigs)
.setOutputDir(outputDir)
.setRunnerJarName(runnerJar.getFileName().toString())
// the path to native-image is not known now, it is only known at the time the native-sources will be consumed
@@ -130,6 +136,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
PackageConfig packageConfig,
CurateOutcomeBuildItem curateOutcomeBuildItem,
List nativeImageProperties,
+ List excludeConfigs,
+ NativeImageAllowIncompleteClasspathAggregateBuildItem incompleteClassPathAllowed,
List nativeImageSecurityProviders,
Optional processInheritIODisabled) {
if (nativeConfig.debug.enabled) {
@@ -187,6 +195,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
.setNativeConfig(nativeConfig)
.setOutputTargetBuildItem(outputTargetBuildItem)
.setNativeImageProperties(nativeImageProperties)
+ .setExcludeConfigs(excludeConfigs)
+ .setBrokenClasspath(incompleteClassPathAllowed.isAllow())
.setNativeImageSecurityProviders(nativeImageSecurityProviders)
.setOutputDir(outputDir)
.setRunnerJarName(runnerJarName)
@@ -477,6 +487,7 @@ static class Builder {
private NativeConfig nativeConfig;
private OutputTargetBuildItem outputTargetBuildItem;
private List nativeImageProperties;
+ private List excludeConfigs;
private List nativeImageSecurityProviders;
private Path outputDir;
private String runnerJarName;
@@ -484,6 +495,7 @@ static class Builder {
private boolean isContainerBuild = false;
private GraalVM.Version graalVMVersion = GraalVM.Version.UNVERSIONED;
private String nativeImageName;
+ private boolean classpathIsBroken;
public Builder setNativeConfig(NativeConfig nativeConfig) {
this.nativeConfig = nativeConfig;
@@ -500,6 +512,16 @@ public Builder setNativeImageProperties(List
return this;
}
+ public Builder setBrokenClasspath(boolean classpathIsBroken) {
+ this.classpathIsBroken = classpathIsBroken;
+ return this;
+ }
+
+ public Builder setExcludeConfigs(List excludeConfigs) {
+ this.excludeConfigs = excludeConfigs;
+ return this;
+ }
+
public Builder setNativeImageSecurityProviders(
List nativeImageSecurityProviders) {
this.nativeImageSecurityProviders = nativeImageSecurityProviders;
@@ -584,8 +606,6 @@ public NativeImageInvokerInfo build() {
"-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime"); //the default collection policy results in full GC's 50% of the time
nativeImageArgs.add("-H:+JNI");
nativeImageArgs.add("-H:+AllowFoldMethods");
- nativeImageArgs.add("-jar");
- nativeImageArgs.add(runnerJarName);
if (nativeConfig.enableFallbackImages) {
nativeImageArgs.add("-H:FallbackThreshold=5");
@@ -595,6 +615,10 @@ public NativeImageInvokerInfo build() {
nativeImageArgs.add("-H:FallbackThreshold=0");
}
+ if (classpathIsBroken) {
+ nativeImageArgs.add("--allow-incomplete-classpath");
+ }
+
if (nativeConfig.reportErrorsAtRuntime) {
nativeImageArgs.add("-H:+ReportUnsupportedElementsAtRuntime");
}
@@ -700,10 +724,21 @@ public NativeImageInvokerInfo build() {
.collect(Collectors.joining(","));
nativeImageArgs.add("-H:AdditionalSecurityProviders=" + additionalSecurityProviders);
}
+
+ // --exclude-config options
+ for (ExcludeConfigBuildItem excludeConfig : excludeConfigs) {
+ nativeImageArgs.add("--exclude-config");
+ nativeImageArgs.add(excludeConfig.getJarFile());
+ nativeImageArgs.add(excludeConfig.getResourceName());
+ }
}
nativeImageArgs.add(nativeImageName);
+ //Make sure to have the -jar as last one, as it otherwise breaks "--exclude-config"
+ nativeImageArgs.add("-jar");
+ nativeImageArgs.add(runnerJarName);
+
return new NativeImageInvokerInfo(nativeImageArgs);
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java
index 76baaac11caab..0eacb06801099 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java
@@ -2,9 +2,7 @@
import static io.quarkus.deployment.steps.ConfigBuildSteps.SERVICES_PREFIX;
import static io.quarkus.deployment.util.ServiceUtil.classNamesNamedIn;
-import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
import java.io.IOException;
import java.lang.reflect.Modifier;
@@ -31,7 +29,6 @@
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.AdditionalBootstrapConfigSourceProviderBuildItem;
import io.quarkus.deployment.builditem.AdditionalStaticInitConfigSourceProviderBuildItem;
-import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.ConfigurationBuildItem;
import io.quarkus.deployment.builditem.ConfigurationTypeBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
@@ -54,7 +51,6 @@
import io.quarkus.runtime.configuration.ConfigChangeRecorder;
import io.quarkus.runtime.configuration.ConfigurationRuntimeConfig;
import io.quarkus.runtime.configuration.RuntimeOverrideConfigSource;
-import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.config.PropertiesLocationConfigSourceFactory;
@@ -92,8 +88,7 @@ void generateConfigClass(
LiveReloadBuildItem liveReloadBuildItem,
List additionalBootstrapConfigSourceProviders,
List staticInitConfigSourceProviders,
- List staticInitConfigSourceFactories,
- List configClasses)
+ List staticInitConfigSourceFactories)
throws IOException {
if (liveReloadBuildItem.isLiveReload()) {
@@ -135,7 +130,6 @@ void generateConfigClass(
.setRuntimeConfigSources(discoveredConfigSources)
.setRuntimeConfigSourceProviders(discoveredConfigSourceProviders)
.setRuntimeConfigSourceFactories(discoveredConfigSourceFactories)
- .setConfigMappings(getConfigClassesWithPrefix(configClasses))
.build()
.run();
}
@@ -249,11 +243,4 @@ private static Set staticSafeServices(Set services) {
}
return staticSafe;
}
-
- private static Set getConfigClassesWithPrefix(List configClasses) {
- return configClasses.stream()
- .filter(ConfigClassBuildItem::isMapping)
- .map(configMapping -> configClassWithPrefix(configMapping.getConfigClass(), configMapping.getPrefix()))
- .collect(toSet());
- }
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAllowIncompleteClasspathAggregateStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAllowIncompleteClasspathAggregateStep.java
new file mode 100644
index 0000000000000..d30507eaad93a
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAllowIncompleteClasspathAggregateStep.java
@@ -0,0 +1,38 @@
+package io.quarkus.deployment.steps;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.jboss.logging.Logger;
+
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathAggregateBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathBuildItem;
+
+@SuppressWarnings("deprecation")
+public final class NativeImageAllowIncompleteClasspathAggregateStep {
+
+ private static final Logger log = Logger.getLogger(NativeImageAllowIncompleteClasspathAggregateStep.class);
+
+ @BuildStep
+ NativeImageAllowIncompleteClasspathAggregateBuildItem aggregateIndividualItems(
+ List list) {
+ if (list.isEmpty()) {
+ return new NativeImageAllowIncompleteClasspathAggregateBuildItem(false);
+ } else {
+ final String extensionsRequiringBrokenClasspath = list.stream()
+ .map(NativeImageAllowIncompleteClasspathBuildItem::getExtensionName)
+ .collect(Collectors.joining(","));
+ log.warn("The following extensions have required the '--allow-incomplete-classpath' flag to be set: {"
+ + extensionsRequiringBrokenClasspath
+ + "}. This is a global flag which might have unexpected effects on other extensions as well, and is a hint of the library "
+ +
+ "needing some additional refactoring to better support GraalVM native-image. In the case of 3rd party dependencies and/or"
+ +
+ " proprietary code there is not much we can do - please ask for support to your library vendor." +
+ " If you incur in any problem with other Quarkus extensions, please try reproducing the problem without these extensions first.");
+ return new NativeImageAllowIncompleteClasspathAggregateBuildItem(true);
+ }
+ }
+
+}
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java
index b362ff6e39826..c9d2c35e3088c 100644
--- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java
+++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java
@@ -191,15 +191,6 @@ public static void addSourceFactoryProvider(SmallRyeConfigBuilder builder, Confi
builder.withSources(provider.getConfigSourceFactory(Thread.currentThread().getContextClassLoader()));
}
- public static void addMapping(SmallRyeConfigBuilder builder, String mappingClass, String prefix) {
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- try {
- builder.withMapping(contextClassLoader.loadClass(mappingClass), prefix);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
/**
* Checks if a property is present in the current Configuration.
*
diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java
index 5e3b2a1254a62..11c55b6fe0a9b 100644
--- a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java
+++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java
@@ -2,13 +2,15 @@
import io.quarkus.cli.Version;
import io.quarkus.maven.ArtifactCoords;
-import io.quarkus.maven.StreamCoords;
import io.quarkus.platform.tools.ToolsConstants;
+import io.quarkus.registry.catalog.PlatformStreamCoords;
import picocli.CommandLine;
import picocli.CommandLine.Model.CommandSpec;
public class TargetQuarkusVersionGroup {
- StreamCoords streamCoords = null;
+ final static String FULL_EXAMPLE = ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":"
+ + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":2.2.0.Final";
+ PlatformStreamCoords streamCoords = null;
String validStream = null;
ArtifactCoords platformBom = null;
@@ -18,12 +20,12 @@ public class TargetQuarkusVersionGroup {
CommandSpec spec;
@CommandLine.Option(paramLabel = "platformKey:streamId", names = { "-S",
- "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:999-SNAPSHOT%n io.quarkus.platform:2.0")
+ "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:2.0")
void setStream(String stream) {
stream = stream.trim();
if (!stream.isEmpty()) {
try {
- streamCoords = StreamCoords.fromString(stream);
+ streamCoords = PlatformStreamCoords.fromString(stream);
validStream = stream;
} catch (IllegalArgumentException iex) {
throw new CommandLine.ParameterException(spec.commandLine(),
@@ -34,7 +36,12 @@ void setStream(String stream) {
}
@CommandLine.Option(paramLabel = "groupId:artifactId:version", names = { "-P",
- "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n io.quarkus:quarkus-bom:2.0.0.Final")
+ "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n"
+ + " " + FULL_EXAMPLE + "%n"
+ + " io.quarkus::999-SNAPSHOT"
+ + " 2.2.0.Final%n"
+ + "Default groupId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + "%n"
+ + "Default artifactId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + "%n")
void setPlatformBom(String bom) {
bom = bom.replaceFirst("^::", "").trim();
if (!bom.isEmpty()) {
@@ -87,7 +94,7 @@ public boolean isStreamSpecified() {
return streamCoords != null;
}
- public StreamCoords getStream() {
+ public PlatformStreamCoords getStream() {
return streamCoords;
}
diff --git a/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java
index 9b0b62eb44fe1..e2d3f8593cc34 100644
--- a/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java
+++ b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java
@@ -5,8 +5,8 @@
import io.quarkus.cli.Version;
import io.quarkus.maven.ArtifactCoords;
-import io.quarkus.maven.StreamCoords;
import io.quarkus.platform.tools.ToolsConstants;
+import io.quarkus.registry.catalog.PlatformStreamCoords;
public class TargetQuarkusVersionGroupTest {
final static String clientVersion = Version.clientVersion();
@@ -64,7 +64,7 @@ void testPlatformUseDefaultGroupVersion() {
void testStreamUseDFullyQualified() {
qvg.setStream("stream-platform:stream-version");
- StreamCoords coords = qvg.getStream();
+ PlatformStreamCoords coords = qvg.getStream();
Assertions.assertEquals("stream-platform", coords.getPlatformKey());
Assertions.assertEquals("stream-version", coords.getStreamId());
}
@@ -73,7 +73,7 @@ void testStreamUseDFullyQualified() {
void testStreamUseDefaultPlatformKey() {
qvg.setStream(":stream-version");
- StreamCoords coords = qvg.getStream();
+ PlatformStreamCoords coords = qvg.getStream();
Assertions.assertNull(coords.getPlatformKey());
Assertions.assertEquals("stream-version", coords.getStreamId());
}
@@ -82,7 +82,7 @@ void testStreamUseDefaultPlatformKey() {
void testStreamUseDefaultStreamId() {
qvg.setStream("stream-platform:");
- StreamCoords coords = qvg.getStream();
+ PlatformStreamCoords coords = qvg.getStream();
Assertions.assertEquals("stream-platform", coords.getPlatformKey());
Assertions.assertEquals("", coords.getStreamId());
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
index 0130082fdd675..c2f3d735004a5 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
@@ -33,7 +33,6 @@
import io.quarkus.gradle.builder.QuarkusModelBuilder;
import io.quarkus.gradle.dependency.ApplicationDeploymentClasspathBuilder;
import io.quarkus.gradle.dependency.ConditionalDependenciesEnabler;
-import io.quarkus.gradle.dependency.ExtensionDependency;
import io.quarkus.gradle.extension.QuarkusPluginExtension;
import io.quarkus.gradle.extension.SourceSetExtension;
import io.quarkus.gradle.tasks.QuarkusAddExtension;
@@ -121,7 +120,7 @@ private void registerTasks(Project project, QuarkusPluginExtension quarkusExt) {
Task quarkusBuild = tasks.create(QUARKUS_BUILD_TASK_NAME, QuarkusBuild.class);
quarkusBuild.dependsOn(quarkusGenerateCode);
- Task quarkusDev = tasks.create(QUARKUS_DEV_TASK_NAME, QuarkusDev.class);
+ QuarkusDev quarkusDev = tasks.create(QUARKUS_DEV_TASK_NAME, QuarkusDev.class);
Task quarkusRemoteDev = tasks.create(QUARKUS_REMOTE_DEV_TASK_NAME, QuarkusRemoteDev.class);
Task quarkusTest = tasks.create(QUARKUS_TEST_TASK_NAME, QuarkusTest.class);
tasks.create(QUARKUS_TEST_CONFIG_TASK_NAME, QuarkusTestConfig.class);
@@ -233,6 +232,7 @@ public void execute(Task test) {
});
project.getPlugins().withId("org.jetbrains.kotlin.jvm", plugin -> {
+ quarkusDev.shouldPropagateJavaCompilerArgs(false);
tasks.getByName("compileKotlin").dependsOn(quarkusGenerateCode);
tasks.getByName("compileTestKotlin").dependsOn(quarkusGenerateCodeTests);
});
@@ -272,15 +272,14 @@ private void afterEvaluate(Project project) {
ConditionalDependenciesEnabler conditionalDependenciesEnabler = new ConditionalDependenciesEnabler(project);
ApplicationDeploymentClasspathBuilder deploymentClasspathBuilder = new ApplicationDeploymentClasspathBuilder(project);
- Set commonExtensions = conditionalDependenciesEnabler
+ conditionalDependenciesEnabler
.declareConditionalDependencies(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
- deploymentClasspathBuilder.createBuildClasspath(commonExtensions, JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
- deploymentClasspathBuilder.addCommonExtension(commonExtensions);
+ deploymentClasspathBuilder.createBuildClasspath(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, true);
for (String baseConfiguration : CONDITIONAL_DEPENDENCY_LOOKUP) {
- Set extensionDependencies = conditionalDependenciesEnabler
+ conditionalDependenciesEnabler
.declareConditionalDependencies(baseConfiguration);
- deploymentClasspathBuilder.createBuildClasspath(extensionDependencies, baseConfiguration);
+ deploymentClasspathBuilder.createBuildClasspath(baseConfiguration, false);
}
final HashSet visited = new HashSet<>();
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java
index e9e53d11a2e59..f49fe94a7138f 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java
@@ -22,25 +22,25 @@ public static String toDeploymentConfigurationName(String baseConfigurationName)
return baseConfigurationName + DEPLOYMENT_CONFIGURATION_SUFFIX;
}
- public void createBuildClasspath(Set extensions, String baseConfigurationName) {
+ public void createBuildClasspath(String baseConfigurationName, boolean common) {
String deploymentConfigurationName = toDeploymentConfigurationName(baseConfigurationName);
project.getConfigurations().create(deploymentConfigurationName);
DependencyHandler dependencies = project.getDependencies();
- for (ExtensionDependency extension : extensions) {
- if (commonExtensions.contains(extension)) {
+ Set firstLevelExtensions = DependencyUtils.loadQuarkusExtension(project,
+ project.getConfigurations().findByName(baseConfigurationName));
+ for (ExtensionDependency extension : firstLevelExtensions) {
+ if (common) {
+ commonExtensions.add(extension);
+ } else if (commonExtensions.contains(extension)) {
continue;
}
extension.createDeploymentVariant(dependencies);
- createDeploymentClasspath(deploymentConfigurationName, extension, dependencies);
+ requireDeploymentDependency(deploymentConfigurationName, extension, dependencies);
}
}
- public void addCommonExtension(Set commonExtensions) {
- this.commonExtensions.addAll(commonExtensions);
- }
-
- private void createDeploymentClasspath(String deploymentConfigurationName, ExtensionDependency extension,
+ private void requireDeploymentDependency(String deploymentConfigurationName, ExtensionDependency extension,
DependencyHandler dependencies) {
ExternalDependency dependency = (ExternalDependency) dependencies.add(deploymentConfigurationName,
extension.asDependencyNotation());
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java
index fcb942ff7dbc9..39414d74335b7 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java
@@ -1,66 +1,46 @@
package io.quarkus.gradle.dependency;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
-import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedArtifact;
-import io.quarkus.bootstrap.BootstrapConstants;
-import io.quarkus.bootstrap.model.AppArtifactCoords;
-import io.quarkus.bootstrap.model.AppArtifactKey;
-import io.quarkus.bootstrap.util.BootstrapUtils;
-import io.quarkus.bootstrap.util.ZipUtils;
-
public class ConditionalDependenciesEnabler {
private final Map featureVariants = new HashMap<>();
+
private final Project project;
public ConditionalDependenciesEnabler(Project project) {
this.project = project;
}
- public Set declareConditionalDependencies(String baseConfigurationName) {
+ public void declareConditionalDependencies(String baseConfigurationName) {
featureVariants.clear();
Configuration resolvedConfiguration = DependencyUtils.duplicateConfiguration(project,
project.getConfigurations().getByName(baseConfigurationName));
+
Set runtimeArtifacts = resolvedConfiguration.getResolvedConfiguration().getResolvedArtifacts();
List extensions = collectExtensionsForResolution(runtimeArtifacts);
featureVariants.putAll(extractFeatureVariants(extensions));
resolveConditionalDependencies(extensions, resolvedConfiguration, baseConfigurationName);
-
- Configuration resolvedExtensionsConfiguration = DependencyUtils.duplicateConfiguration(project,
- project.getConfigurations().getByName(baseConfigurationName));
- Set enabledExtension = getEnabledExtension(resolvedExtensionsConfiguration);
- project.getConfigurations().remove(resolvedExtensionsConfiguration);
-
- return enabledExtension;
}
private List collectExtensionsForResolution(Set runtimeArtifacts) {
List firstLevelExtensions = new ArrayList<>();
for (ResolvedArtifact artifact : runtimeArtifacts) {
- ExtensionDependency extension = getExtensionInfoOrNull(artifact);
+ ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact);
if (extension != null) {
if (!extension.conditionalDependencies.isEmpty()) {
if (extension.needsResolution(runtimeArtifacts)) {
@@ -72,43 +52,19 @@ private List collectExtensionsForResolution(Set getEnabledExtension(Configuration classpath) {
- Set enabledExtensions = new HashSet<>();
- for (ResolvedArtifact artifact : classpath.getResolvedConfiguration().getResolvedArtifacts()) {
- ExtensionDependency extension = getExtensionInfoOrNull(artifact);
- if (extension != null) {
- enabledExtensions.add(extension);
- }
- }
- return enabledExtensions;
- }
-
- private Map extractFeatureVariants(List extensions) {
- Map possibleVariant = new HashMap<>();
- for (ExtensionDependency extension : extensions) {
- for (Dependency dependency : extension.conditionalDependencies) {
- possibleVariant.put(DependencyUtils.asFeatureName(dependency), extension);
- }
- }
- return possibleVariant;
- }
-
private void resolveConditionalDependencies(List conditionalExtensions,
Configuration existingDependencies, String baseConfigurationName) {
final Configuration conditionalDeps = createConditionalDependenciesConfiguration(existingDependencies,
conditionalExtensions);
boolean hasChanged = false;
- Map validConditionalDependencies = new HashMap<>();
List newConditionalDependencies = new ArrayList<>();
newConditionalDependencies.addAll(conditionalExtensions);
for (ResolvedArtifact artifact : conditionalDeps.getResolvedConfiguration().getResolvedArtifacts()) {
- ExtensionDependency extensionDependency = getExtensionInfoOrNull(artifact);
+ ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact);
if (extensionDependency != null) {
if (DependencyUtils.exist(conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(),
extensionDependency.dependencyConditions)) {
enableConditionalDependency(extensionDependency.extensionId);
- validConditionalDependencies.put(DependencyUtils.asFeatureName(extensionDependency.extensionId),
- extensionDependency);
if (!extensionDependency.conditionalDependencies.isEmpty()) {
featureVariants.putAll(extractFeatureVariants(Collections.singletonList(extensionDependency)));
}
@@ -130,6 +86,16 @@ private void resolveConditionalDependencies(List conditiona
}
}
+ private Map extractFeatureVariants(List extensions) {
+ Map possibleVariant = new HashMap<>();
+ for (ExtensionDependency extension : extensions) {
+ for (Dependency dependency : extension.conditionalDependencies) {
+ possibleVariant.put(DependencyUtils.asFeatureName(dependency), extension);
+ }
+ }
+ return possibleVariant;
+ }
+
private Configuration createConditionalDependenciesConfiguration(Configuration existingDeps,
List extensions) {
Configuration newConfiguration = existingDeps.copy();
@@ -153,55 +119,4 @@ private void enableConditionalDependency(ModuleVersionIdentifier dependency) {
}
extension.importConditionalDependency(project.getDependencies(), dependency);
}
-
- private ExtensionDependency getExtensionInfoOrNull(ResolvedArtifact artifact) {
- ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId();
- File artifactFile = artifact.getFile();
- if (!artifactFile.exists() || !"jar".equals(artifact.getExtension())) {
- return null;
- }
- if (artifactFile.isDirectory()) {
- Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH);
- if (Files.exists(descriptorPath)) {
- return loadExtensionInfo(descriptorPath, artifactId);
- }
- } else {
- try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) {
- Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH);
- if (Files.exists(descriptorPath)) {
- return loadExtensionInfo(descriptorPath, artifactId);
- }
- } catch (IOException e) {
- throw new GradleException("Failed to read " + artifactFile, e);
- }
- }
- return null;
- }
-
- private ExtensionDependency loadExtensionInfo(Path descriptorPath, ModuleVersionIdentifier exentionId) {
- final Properties extensionProperties = new Properties();
- try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) {
- extensionProperties.load(reader);
- } catch (IOException e) {
- throw new GradleException("Failed to load " + descriptorPath, e);
- }
- AppArtifactCoords deploymentModule = AppArtifactCoords
- .fromString(extensionProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT));
- final List conditionalDependencies;
- if (extensionProperties.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) {
- final String[] deps = BootstrapUtils
- .splitByWhitespace(extensionProperties.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES));
- conditionalDependencies = new ArrayList<>(deps.length);
- for (String conditionalDep : deps) {
- conditionalDependencies.add(DependencyUtils.create(project.getDependencies(), conditionalDep));
- }
- } else {
- conditionalDependencies = Collections.emptyList();
- }
-
- final AppArtifactKey[] constraints = BootstrapUtils
- .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION));
- return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies,
- constraints == null ? Collections.emptyList() : Arrays.asList(constraints));
- }
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java
index d10ba758e398c..91e17dbe0bf19 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java
@@ -1,10 +1,20 @@
package io.quarkus.gradle.dependency;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Properties;
import java.util.Set;
+import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.UnknownDomainObjectException;
import org.gradle.api.artifacts.Configuration;
@@ -18,8 +28,11 @@
import org.gradle.api.initialization.IncludedBuild;
import org.gradle.api.plugins.JavaPlugin;
+import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.model.AppArtifactCoords;
import io.quarkus.bootstrap.model.AppArtifactKey;
+import io.quarkus.bootstrap.util.BootstrapUtils;
+import io.quarkus.bootstrap.util.ZipUtils;
public class DependencyUtils {
@@ -74,6 +87,21 @@ public static boolean exists(Set runtimeArtifacts, Dependency
return false;
}
+ public static Set loadQuarkusExtension(Project project, Configuration configuration) {
+ Set extensions = new HashSet<>();
+ Configuration configurationCopy = duplicateConfiguration(project, configuration);
+
+ Set resolvedArtifacts = configurationCopy.getResolvedConfiguration().getResolvedArtifacts();
+ for (ResolvedArtifact artifact : resolvedArtifacts) {
+ ExtensionDependency extension = getExtensionInfoOrNull(project, artifact);
+ if (extension != null) {
+ extensions.add(extension);
+ }
+ }
+
+ return extensions;
+ }
+
public static boolean isTestFixtureDependency(Dependency dependency) {
if (!(dependency instanceof ModuleDependency)) {
return false;
@@ -138,4 +166,56 @@ public static String asFeatureName(ModuleVersionIdentifier version) {
public static String asFeatureName(Dependency version) {
return version.getGroup() + ":" + version.getName();
}
+
+ public static ExtensionDependency getExtensionInfoOrNull(Project project, ResolvedArtifact artifact) {
+ ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId();
+ File artifactFile = artifact.getFile();
+ if (!artifactFile.exists() || !"jar".equals(artifact.getExtension())) {
+ return null;
+ }
+ if (artifactFile.isDirectory()) {
+ Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH);
+ if (Files.exists(descriptorPath)) {
+ return loadExtensionInfo(project, descriptorPath, artifactId);
+ }
+ } else {
+ try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) {
+ Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH);
+ if (Files.exists(descriptorPath)) {
+ return loadExtensionInfo(project, descriptorPath, artifactId);
+ }
+ } catch (IOException e) {
+ throw new GradleException("Failed to read " + artifactFile, e);
+ }
+ }
+ return null;
+ }
+
+ private static ExtensionDependency loadExtensionInfo(Project project, Path descriptorPath,
+ ModuleVersionIdentifier exentionId) {
+ final Properties extensionProperties = new Properties();
+ try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) {
+ extensionProperties.load(reader);
+ } catch (IOException e) {
+ throw new GradleException("Failed to load " + descriptorPath, e);
+ }
+ AppArtifactCoords deploymentModule = AppArtifactCoords
+ .fromString(extensionProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT));
+ final List conditionalDependencies;
+ if (extensionProperties.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) {
+ final String[] deps = BootstrapUtils
+ .splitByWhitespace(extensionProperties.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES));
+ conditionalDependencies = new ArrayList<>(deps.length);
+ for (String conditionalDep : deps) {
+ conditionalDependencies.add(DependencyUtils.create(project.getDependencies(), conditionalDep));
+ }
+ } else {
+ conditionalDependencies = Collections.emptyList();
+ }
+
+ final AppArtifactKey[] constraints = BootstrapUtils
+ .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION));
+ return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies,
+ constraints == null ? Collections.emptyList() : Arrays.asList(constraints));
+ }
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
index 3c050bb07db45..f28c8bca01aa5 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
@@ -74,6 +74,8 @@ public class QuarkusDev extends QuarkusTask {
private List compilerArgs = new LinkedList<>();
+ private boolean shouldPropagateJavaCompilerArgs = true;
+
@Inject
public QuarkusDev() {
super("Development mode: enables hot deployment with background compilation");
@@ -303,7 +305,7 @@ private QuarkusDevModeLauncher newLauncher() throws Exception {
builder.targetJavaVersion(javaPluginConvention.getTargetCompatibility().toString());
}
- if (getCompilerArgs().isEmpty()) {
+ if (getCompilerArgs().isEmpty() && shouldPropagateJavaCompilerArgs) {
getJavaCompileTask()
.map(compileTask -> compileTask.getOptions().getCompilerArgs())
.ifPresent(builder::compilerOptions);
@@ -571,4 +573,8 @@ private void addToClassPaths(GradleDevModeLauncher.Builder classPathManifest, Fi
classPathManifest.classpathEntry(file);
}
}
+
+ public void shouldPropagateJavaCompilerArgs(boolean shouldPropagateJavaCompilerArgs) {
+ this.shouldPropagateJavaCompilerArgs = shouldPropagateJavaCompilerArgs;
+ }
}
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CheckForUpdatesMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CheckForUpdatesMojo.java
new file mode 100644
index 0000000000000..4fbf90c7fdc90
--- /dev/null
+++ b/devtools/maven/src/main/java/io/quarkus/maven/CheckForUpdatesMojo.java
@@ -0,0 +1,412 @@
+package io.quarkus.maven;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.impl.RemoteRepositoryManager;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+
+import io.quarkus.bootstrap.BootstrapConstants;
+import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
+import io.quarkus.devtools.commands.data.QuarkusCommandException;
+import io.quarkus.devtools.project.QuarkusProjectHelper;
+import io.quarkus.maven.utilities.MojoUtils;
+import io.quarkus.platform.tools.maven.MojoMessageWriter;
+import io.quarkus.registry.ExtensionCatalogResolver;
+import io.quarkus.registry.RegistryResolutionException;
+import io.quarkus.registry.catalog.Extension;
+import io.quarkus.registry.catalog.ExtensionCatalog;
+import io.quarkus.registry.catalog.ExtensionOrigin;
+import io.quarkus.registry.catalog.selection.ExtensionOrigins;
+import io.quarkus.registry.catalog.selection.OriginCombination;
+import io.quarkus.registry.catalog.selection.OriginPreference;
+import io.quarkus.registry.catalog.selection.OriginSelector;
+import io.quarkus.registry.util.PlatformArtifacts;
+
+/**
+ * NOTE: this mojo is experimental
+ */
+@Mojo(name = "check-for-updates", requiresProject = true)
+public class CheckForUpdatesMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "${project}")
+ protected MavenProject project;
+
+ @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
+ private List repos;
+
+ @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
+ private RepositorySystemSession repoSession;
+
+ @Component
+ private RepositorySystem repoSystem;
+
+ @Component
+ RemoteRepositoryManager remoteRepoManager;
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+
+ getLog().warn(
+ "This goal is experimental. Its name, parameters, output and implementation will be evolving without the promise of keeping backward compatibility");
+
+ if (project.getFile() == null) {
+ throw new MojoExecutionException("This goal requires a project");
+ }
+
+ if (!QuarkusProjectHelper.isRegistryClientEnabled()) {
+ throw new MojoExecutionException("This goal requires a Quarkus extension registry client to be enabled");
+ }
+
+ final Map previousExtensions = getDirectExtensionDependencies();
+ if (previousExtensions.isEmpty()) {
+ getLog().info("The project does not appear to depend on any Quarkus extension directly");
+ return;
+ }
+
+ final MavenArtifactResolver mvn;
+ try {
+ mvn = MavenArtifactResolver.builder()
+ .setRepositorySystem(repoSystem)
+ .setRepositorySystemSession(
+ getLog().isDebugEnabled() ? repoSession : MojoUtils.muteTransferListener(repoSession))
+ .setRemoteRepositories(repos)
+ .setRemoteRepositoryManager(remoteRepoManager)
+ .build();
+ } catch (Exception e) {
+ throw new MojoExecutionException("Failed to initialize Maven artifact resolver", e);
+ }
+ final MojoMessageWriter log = new MojoMessageWriter(getLog());
+ final ExtensionCatalogResolver catalogResolver;
+ try {
+ catalogResolver = QuarkusProjectHelper.getCatalogResolver(mvn, log);
+ } catch (RegistryResolutionException e) {
+ throw new MojoExecutionException("Failed to initialize Quarkus extension registry client", e);
+ }
+
+ if (!catalogResolver.hasRegistries()) {
+ throw new MojoExecutionException("Configured Quarkus extension registries aren't available");
+ }
+
+ final ExtensionCatalog latestCatalog;
+ try {
+ latestCatalog = catalogResolver.resolveExtensionCatalog();
+ } catch (RegistryResolutionException e) {
+ throw new MojoExecutionException(
+ "Failed to resolve the latest Quarkus extension catalog from the configured extension registries", e);
+ }
+
+ final Map recommendedExtensionMap = new HashMap<>(latestCatalog.getExtensions().size());
+ final Map> recommendedExtensionsByOrigin = new HashMap<>();
+ for (Extension e : latestCatalog.getExtensions()) {
+ recommendedExtensionMap.put(e.getArtifact().getKey(), e);
+ for (ExtensionOrigin origin : e.getOrigins()) {
+ recommendedExtensionsByOrigin.computeIfAbsent(origin.getId(), k -> new ArrayList<>())
+ .add(e.getArtifact().getKey());
+ }
+ }
+
+ final List notAvailableExtensions = new ArrayList<>(0);
+ final List recommendedExtensions = new ArrayList<>(previousExtensions.size());
+ for (ArtifactKey key : previousExtensions.keySet()) {
+ final Extension e = recommendedExtensionMap.get(key);
+ if (e == null) {
+ notAvailableExtensions.add(e);
+ } else {
+ recommendedExtensions.add(e);
+ }
+ }
+
+ if (!notAvailableExtensions.isEmpty()) {
+ final StringBuilder buf = new StringBuilder();
+ buf.append(
+ "Could not find any information about the following extensions in the currently configured registries: ");
+ buf.append(notAvailableExtensions.get(0).getArtifact().getKey().toGacString());
+ for (int i = 1; i < notAvailableExtensions.size(); ++i) {
+ buf.append(", ").append(notAvailableExtensions.get(i).getArtifact().getKey().toGacString());
+ }
+ getLog().warn(buf.toString());
+ return;
+ }
+
+ final List recommendedOrigins;
+ try {
+ recommendedOrigins = getRecommendedOrigins(latestCatalog, recommendedExtensions);
+ } catch (QuarkusCommandException e) {
+ getLog().warn(e.getLocalizedMessage());
+ return;
+ }
+
+ final List previousBomImports = new ArrayList<>();
+ for (Dependency d : project.getDependencyManagement().getDependencies()) {
+ if (PlatformArtifacts.isCatalogArtifactId(d.getArtifactId())) {
+ final ArtifactCoords platformBomCoords = new ArtifactCoords(d.getGroupId(),
+ PlatformArtifacts.ensureBomArtifactId(d.getArtifactId()), "pom", d.getVersion());
+ if (d.getArtifactId().startsWith("quarkus-universe-bom-")) {
+ // in pre-2.x quarkus versions, the quarkus-bom descriptor would show up as a parent of the quarkus-universe-bom one
+ // even if it was not actually imported, so here we simply remove it, if it was found
+ previousBomImports.remove(new ArtifactCoords(platformBomCoords.getGroupId(), "quarkus-bom", "pom",
+ platformBomCoords.getVersion()));
+ }
+ previousBomImports.add(platformBomCoords);
+ }
+ }
+
+ final List recommendedBomImports = new ArrayList<>();
+ final Map nonPlatformUpdates = new LinkedHashMap<>(0);
+
+ for (ExtensionCatalog origin : recommendedOrigins) {
+ if (origin.isPlatform()) {
+ if (!previousBomImports.remove(origin.getBom())) {
+ recommendedBomImports.add(origin.getBom());
+ }
+ for (ArtifactKey extKey : recommendedExtensionsByOrigin.getOrDefault(origin.getId(), Collections.emptyList())) {
+ previousExtensions.remove(extKey);
+ }
+ } else {
+ for (ArtifactKey extKey : recommendedExtensionsByOrigin.getOrDefault(origin.getId(), Collections.emptyList())) {
+ final Extension recommendedExt = recommendedExtensionMap.get(extKey);
+ final String prevVersion = previousExtensions.remove(extKey);
+ if (prevVersion != null && !prevVersion.equals(recommendedExt.getArtifact().getVersion())) {
+ nonPlatformUpdates.put(recommendedExt.getArtifact(), prevVersion);
+ }
+ }
+ }
+ }
+
+ if (recommendedBomImports.isEmpty() && nonPlatformUpdates.isEmpty()) {
+ log.info("The project is up-to-date");
+ return;
+ }
+
+ ArtifactCoords prevPluginCoords = null;
+ for (Plugin p : project.getBuildPlugins()) {
+ if (p.getArtifactId().equals("quarkus-maven-plugin")) {
+ prevPluginCoords = new ArtifactCoords(p.getGroupId(), p.getArtifactId(), p.getVersion());
+ break;
+ }
+ }
+
+ ExtensionCatalog core = null;
+ for (ExtensionOrigin o : recommendedOrigins) {
+ if (o.isPlatform() && o.getBom().getArtifactId().equals("quarkus-bom")) {
+ core = (ExtensionCatalog) o;
+ }
+ }
+
+ ArtifactCoords recommendedPluginCoords = null;
+ if (core != null) {
+ final Map props = (Map) ((Map) core.getMetadata().getOrDefault("project",
+ Collections.emptyMap())).getOrDefault("properties", Collections.emptyMap());
+ final String pluginGroupId = (String) props.get("maven-plugin-groupId");
+ final String pluginArtifactId = (String) props.get("maven-plugin-artifactId");
+ final String pluginVersion = (String) props.get("maven-plugin-version");
+ if (pluginGroupId == null || pluginArtifactId == null || pluginVersion == null) {
+ log.warn("Failed to locate the recommended Quarkus Maven plugin coordinates");
+ } else {
+ recommendedPluginCoords = new ArtifactCoords(pluginGroupId, pluginArtifactId, pluginVersion);
+ }
+ }
+
+ final StringWriter buf = new StringWriter();
+ try (BufferedWriter writer = new BufferedWriter(buf)) {
+
+ writer.append("Currently recommended updates for the application include:");
+ writer.newLine();
+ writer.newLine();
+
+ if (!previousBomImports.isEmpty()) {
+ writer.append(" * BOM imports to be replaced:");
+ writer.newLine();
+ writer.newLine();
+ for (ArtifactCoords bom : previousBomImports) {
+ logBomImport(writer, bom);
+ }
+ writer.newLine();
+ }
+
+ if (!recommendedBomImports.isEmpty()) {
+ writer.append(" * New recommended BOM imports:");
+ writer.newLine();
+ writer.newLine();
+ for (ArtifactCoords bom : recommendedBomImports) {
+ logBomImport(writer, bom);
+ }
+ writer.newLine();
+ }
+
+ if (!nonPlatformUpdates.isEmpty()) {
+ writer.append(" * New recommended extension versions (not managed by the BOMs):");
+ writer.newLine();
+ writer.newLine();
+
+ for (ArtifactCoords coords : nonPlatformUpdates.keySet()) {
+ writer.append(" ");
+ writer.newLine();
+ writer.append(" ").append(coords.getGroupId()).append("");
+ writer.newLine();
+ writer.append(" ").append(coords.getArtifactId()).append("");
+ writer.newLine();
+ writer.append(" ").append(coords.getVersion()).append("");
+ writer.newLine();
+ writer.append(" ");
+ writer.newLine();
+ }
+ writer.newLine();
+ }
+
+ if (prevPluginCoords != null && recommendedPluginCoords != null
+ && !prevPluginCoords.equals(recommendedPluginCoords)) {
+ writer.append(" * Recommended Quarkus Maven plugin:");
+ writer.newLine();
+ writer.newLine();
+ writer.append(" ");
+ writer.newLine();
+ writer.append(" ").append(recommendedPluginCoords.getGroupId()).append("");
+ writer.newLine();
+ writer.append(" ").append(recommendedPluginCoords.getArtifactId()).append("");
+ writer.newLine();
+ writer.append(" ").append(recommendedPluginCoords.getVersion()).append("");
+ writer.newLine();
+ writer.append(" ");
+ writer.newLine();
+
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed to compose the update report", e);
+ }
+
+ getLog().info(buf.toString());
+ }
+
+ private void logBomImport(BufferedWriter writer, ArtifactCoords bom) throws IOException {
+ writer.append(" ");
+ writer.newLine();
+ writer.append(" ").append(bom.getGroupId()).append("");
+ writer.newLine();
+ writer.append(" ").append(bom.getArtifactId()).append("");
+ writer.newLine();
+ writer.append(" ").append(bom.getVersion()).append("");
+ writer.newLine();
+ writer.append(" pom");
+ writer.newLine();
+ writer.append(" import");
+ writer.newLine();
+ writer.append(" ");
+ writer.newLine();
+ }
+
+ private Map getDirectExtensionDependencies() throws MojoExecutionException {
+ final List modelDeps = project.getModel().getDependencies();
+ final List requests = new ArrayList<>(modelDeps.size());
+ for (Dependency d : modelDeps) {
+ if ("jar".equals(d.getType())) {
+ requests.add(new ArtifactRequest().setArtifact(
+ new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()))
+ .setRepositories(repos));
+ }
+ }
+ final List artifactResults;
+ try {
+ artifactResults = repoSystem.resolveArtifacts(repoSession, requests);
+ } catch (ArtifactResolutionException e) {
+ throw new MojoExecutionException("Failed to resolve project dependencies", e);
+ }
+ final Map extensions = new HashMap<>(artifactResults.size());
+ for (ArtifactResult ar : artifactResults) {
+ final Artifact a = ar.getArtifact();
+ if (isExtension(a.getFile().toPath())) {
+ extensions.put(new ArtifactKey(a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getExtension()),
+ a.getVersion());
+ }
+ }
+ return extensions;
+ }
+
+ private static boolean isExtension(Path p) throws MojoExecutionException {
+ if (!Files.exists(p)) {
+ throw new MojoExecutionException("Extension artifact " + p + " does not exist");
+ }
+ if (Files.isDirectory(p)) {
+ return Files.exists(p.resolve(BootstrapConstants.DESCRIPTOR_PATH));
+ } else {
+ try (FileSystem fs = FileSystems.newFileSystem(p, (ClassLoader) null)) {
+ return Files.exists(fs.getPath(BootstrapConstants.DESCRIPTOR_PATH));
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed to read archive " + p, e);
+ }
+ }
+ }
+
+ private static List getRecommendedOrigins(ExtensionCatalog extensionCatalog, List extensions)
+ throws QuarkusCommandException {
+ final List extOrigins = new ArrayList<>(extensions.size());
+ for (Extension e : extensions) {
+ addOrigins(extOrigins, e);
+ }
+ final OriginSelector os = new OriginSelector(extOrigins);
+ os.calculateCompatibleCombinations();
+
+ final OriginCombination recommendedCombination = os.getRecommendedCombination();
+ if (recommendedCombination == null) {
+ final StringBuilder buf = new StringBuilder();
+ buf.append("Failed to determine a compatible Quarkus version for the requested extensions: ");
+ buf.append(extensions.get(0).getArtifact().getKey().toGacString());
+ for (int i = 1; i < extensions.size(); ++i) {
+ buf.append(", ").append(extensions.get(i).getArtifact().getKey().toGacString());
+ }
+ throw new QuarkusCommandException(buf.toString());
+ }
+ return recommendedCombination.getUniqueSortedOrigins().stream().map(o -> o.getCatalog()).collect(Collectors.toList());
+ }
+
+ private static void addOrigins(final List extOrigins, Extension e) {
+ ExtensionOrigins.Builder eoBuilder = null;
+ for (ExtensionOrigin o : e.getOrigins()) {
+ if (!(o instanceof ExtensionCatalog)) {
+ continue;
+ }
+ final ExtensionCatalog c = (ExtensionCatalog) o;
+ final OriginPreference op = (OriginPreference) c.getMetadata().get("origin-preference");
+ if (op == null) {
+ continue;
+ }
+ if (eoBuilder == null) {
+ eoBuilder = ExtensionOrigins.builder(e.getArtifact().getKey());
+ }
+ eoBuilder.addOrigin(c, op);
+ }
+ if (eoBuilder != null) {
+ extOrigins.add(eoBuilder.build());
+ }
+ }
+}
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
index e5e6a77cd8117..12df08a031baa 100644
--- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
+++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
@@ -251,7 +251,7 @@ public void execute() throws MojoExecutionException {
}
} else if (containsAtLeastOneGradleFile) {
throw new MojoExecutionException(
- "You are trying to create maven project in a directory that contains only gradle build files.");
+ "You are trying to create a Maven project in a directory that contains only Gradle build files.");
}
} else if (BuildTool.GRADLE.equals(buildToolEnum) || BuildTool.GRADLE_KOTLIN_DSL.equals(buildToolEnum)) {
if (containsAtLeastOneGradleFile) {
diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html
new file mode 100644
index 0000000000000..0317faba62263
--- /dev/null
+++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html
@@ -0,0 +1,3 @@
+{#include index-entry}
+{#body}This codestart implements a simple gRPC service that can be tested in the Dev UI (available in dev mode only).
+{/include}
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/cli-tooling.adoc b/docs/src/main/asciidoc/cli-tooling.adoc
index e2a39ca359959..c6ef10ea64b00 100644
--- a/docs/src/main/asciidoc/cli-tooling.adoc
+++ b/docs/src/main/asciidoc/cli-tooling.adoc
@@ -173,12 +173,12 @@ Both `quarkus create` and `quarkus extension list` allow you to explicitly speci
1. Specify a specific Platform Release BOM
+
-A https://quarkus.io/guides/platform#quarkus-platform-bom[Quarkus Platform release BOM] is identified by `groupId:artifactId:version` (GAV) coordinates. When specifying a platform release BOM, you may use empty segments to fallback to default values (groupId: `io.quarkus`, artifactId: `quarkus-bom`, version: cli version). If you specify only one segment (no `:`), it is assumed to be a version.
+A https://quarkus.io/guides/platform#quarkus-platform-bom[Quarkus Platform release BOM] is identified by `groupId:artifactId:version` (GAV) coordinates. When specifying a platform release BOM, you may use empty segments to fallback to default values (shown with `quarkus create app --help`). If you specify only one segment (no `:`), it is assumed to be a version.
+
For example:
+
-- Given the `2.0.0.Final` version of the CLI, specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus.platform:quarkus-bom:2.0.0.Final`.
-- Specifying `-P 999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT`
+- With the `2.0.0.Final` version of the CLI, specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus:quarkus-bom:2.0.0.Final`. Specifying `-P 999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT`.
+- With the `2.1.0.Final` version of the CLI, `io.quarkus.platform` is the default group id. Specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus.platform:quarkus-bom:2.1.0.Final`. Note that you need to specify the group id to work with a snapshot, e.g `-P io.quarkus::999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT`.
+
Note: default values are subject to change. Using the `--dry-run` option will show you the computed value.
diff --git a/docs/src/main/asciidoc/images/dev-ui-oidc-card.png b/docs/src/main/asciidoc/images/dev-ui-oidc-card.png
index 5bd40cab30bc6..b972a88330a7b 100644
Binary files a/docs/src/main/asciidoc/images/dev-ui-oidc-card.png and b/docs/src/main/asciidoc/images/dev-ui-oidc-card.png differ
diff --git a/docs/src/main/asciidoc/images/dev-ui-oidc-keycloak-card.png b/docs/src/main/asciidoc/images/dev-ui-oidc-keycloak-card.png
index e249301b3cb02..f88c3bbf8b027 100644
Binary files a/docs/src/main/asciidoc/images/dev-ui-oidc-keycloak-card.png and b/docs/src/main/asciidoc/images/dev-ui-oidc-keycloak-card.png differ
diff --git a/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc b/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc
index ee4109b2640bf..e31ad1b5aa98e 100644
--- a/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc
+++ b/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc
@@ -45,7 +45,7 @@ However, you can go right to the completed example.
Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive].
-The solution is located in the `kafka-avro-schema-quickstart` {quickstarts-tree-url}/kafka-quickstart-avro-schema[directory].
+The solution is located in the `kafka-avro-schema-quickstart` {quickstarts-tree-url}/kafka-avro-schema-quickstart[directory].
== Creating the Maven Project
diff --git a/docs/src/main/asciidoc/mailer-reference.adoc b/docs/src/main/asciidoc/mailer-reference.adoc
index d6e9af7836621..544b0a771c6e3 100644
--- a/docs/src/main/asciidoc/mailer-reference.adoc
+++ b/docs/src/main/asciidoc/mailer-reference.adoc
@@ -288,6 +288,11 @@ You can also create your own instance, and pass your own configuration.
If you want to use the Gmail SMTP server, first create a dedicated password in `Google Account > Security > App passwords` or go to https://myaccount.google.com/apppasswords.
+[NOTE]
+====
+You need to switch on 2-Step Verification at https://myaccount.google.com/security in order to access the App passwords page.
+====
+
When done, you can configure your Quarkus application by adding the following properties to your `application.properties`:
With TLS:
diff --git a/docs/src/main/asciidoc/mailer.adoc b/docs/src/main/asciidoc/mailer.adoc
index 2bde3fce3ea53..d61e20586f862 100644
--- a/docs/src/main/asciidoc/mailer.adoc
+++ b/docs/src/main/asciidoc/mailer.adoc
@@ -184,7 +184,7 @@ The Quarkus mailer is using SMTP, so make sure you have access to a SMTP server.
In the `src/main/resources/application.properties` file, you need to configure the host, port, username, password as well as the other configuration aspect.
Note that the password can also be configured using system properties and environment variables.
-See the xref:configuration-reference.adoc[configuration reference guide] for details.
+See the xref:config-reference.adoc[configuration reference guide] for details.
Here is an example using _sendgrid_:
diff --git a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc
index 0fa38d53ae5df..614f14aa451af 100644
--- a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc
+++ b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc
@@ -318,7 +318,9 @@ This interface allows you to dynamically create tenant configurations at runtime
package io.quarkus.it.keycloak;
import javax.enterprise.context.ApplicationScoped;
+import java.util.function.Supplier;
+import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.OidcTenantConfig;
import io.quarkus.oidc.TenantConfigResolver;
import io.vertx.ext.web.RoutingContext;
@@ -327,7 +329,7 @@ import io.vertx.ext.web.RoutingContext;
public class CustomTenantConfigResolver implements TenantConfigResolver {
@Override
- public OidcTenantConfig resolve(RoutingContext context) {
+ public Uni resolve(RoutingContext context, TenantConfigResolver.TenantConfigRequestContext requestContext) {
String path = context.request().path();
String[] parts = path.split("/");
@@ -337,24 +339,30 @@ public class CustomTenantConfigResolver implements TenantConfigResolver {
}
if ("tenant-c".equals(parts[1])) {
- OidcTenantConfig config = new OidcTenantConfig();
+ // Do 'return requestContext.runBlocking(createTenantConfig());'
+ // if a blocking call is required to create a tenant config
+ return Uni.createFromItem(createTenantConfig());
+ }
+
+ // resolve to default tenant configuration
+ return null;
+ }
- config.setTenantId("tenant-c");
- config.setAuthServerUrl("http://localhost:8180/auth/realms/tenant-c");
- config.setClientId("multi-tenant-client");
- OidcTenantConfig.Credentials credentials = new OidcTenantConfig.Credentials();
+ private Supplier createTenantConfig() {
+ final OidcTenantConfig config = new OidcTenantConfig();
- credentials.setSecret("my-secret");
+ config.setTenantId("tenant-c");
+ config.setAuthServerUrl("http://localhost:8180/auth/realms/tenant-c");
+ config.setClientId("multi-tenant-client");
+ OidcTenantConfig.Credentials credentials = new OidcTenantConfig.Credentials();
- config.setCredentials(credentials);
+ credentials.setSecret("my-secret");
- // any other setting support by the quarkus-oidc extension
+ config.setCredentials(credentials);
- return config;
- }
+ // any other setting support by the quarkus-oidc extension
- // resolve to default tenant configuration
- return null;
+ return () -> config;
}
}
----
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 0a6f47b01151c..62671a68ace68 100644
--- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc
+++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc
@@ -469,6 +469,59 @@ In such cases, you can use `quarkus.oidc.token-state-manager.split-tokens=true`
Register your own `io.quarkus.oidc.TokenStateManager' implementation as an `@ApplicationScoped` CDI bean if you need to customize the way the tokens are associated with the session cookie. For example, you may want to keep the tokens in a database and have only a database pointer stored in a session cookie. Note though that it may present some challenges in making the tokens available across multiple microservices nodes.
+Here is a simple example:
+
+[source, java]
+----
+package io.quarkus.oidc.test;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import io.quarkus.arc.AlternativePriority;
+import io.quarkus.oidc.AuthorizationCodeTokens;
+import io.quarkus.oidc.OidcTenantConfig;
+import io.quarkus.oidc.TokenStateManager;
+import io.quarkus.oidc.runtime.DefaultTokenStateManager;
+import io.smallrye.mutiny.Uni;
+import io.vertx.ext.web.RoutingContext;
+
+@ApplicationScoped
+@AlternativePriority(1)
+public class CustomTokenStateManager implements TokenStateManager {
+
+ @Inject
+ DefaultTokenStateManager tokenStateManager;
+
+ @Override
+ public Uni createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig,
+ AuthorizationCodeTokens sessionContent, TokenStateManager.CreateTokenStateRequestContext requestContext) {
+ return tokenStateManager.createTokenState(routingContext, oidcConfig, sessionContent, requestContext)
+ .map(t -> (t + "|custom"));
+ }
+
+ @Override
+ public Uni getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig,
+ String tokenState, TokenStateManager.GetTokensRequestContext requestContext) {
+ if (!tokenState.endsWith("|custom")) {
+ throw new IllegalStateException();
+ }
+ String defaultState = tokenState.substring(0, tokenState.length() - 7);
+ return tokenStateManager.getTokens(routingContext, oidcConfig, defaultState, requestContext);
+ }
+
+ @Override
+ public Uni deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState,
+ TokenStateManager.DeleteTokensRequestContext requestContext) {
+ if (!tokenState.endsWith("|custom")) {
+ throw new IllegalStateException();
+ }
+ String defaultState = tokenState.substring(0, tokenState.length() - 7);
+ return tokenStateManager.deleteTokens(routingContext, oidcConfig, defaultState, requestContext);
+ }
+}
+----
+
== Listening to important authentication events
One can register `@ApplicationScoped` bean which will observe important OIDC authentication events. The listener will be updated when a user has logged in for the first time or re-authenticated, as well as when the session has been refreshed. More events may be reported in the future. For example:
diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java
index d586ddb8bb388..0d4b4c50470a5 100644
--- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java
+++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java
@@ -47,6 +47,12 @@ public class DataSourceJdbcRuntimeConfig {
@ConfigItem(defaultValue = "2M")
public Optional backgroundValidationInterval = Optional.of(Duration.ofMinutes(2));
+ /**
+ * Perform foreground validation on connections that have been idle for longer than the specified interval.
+ */
+ @ConfigItem
+ public Optional foregroundValidationInterval = Optional.empty();
+
/**
* The timeout before cancelling the acquisition of a new connection
*/
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 e5a1d8c5e9e31..fa6c4290da30e 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
@@ -276,6 +276,9 @@ private void applyNewConfiguration(AgroalDataSourceConfigurationSupplier dataSou
if (dataSourceJdbcRuntimeConfig.backgroundValidationInterval.isPresent()) {
poolConfiguration.validationTimeout(dataSourceJdbcRuntimeConfig.backgroundValidationInterval.get());
}
+ if (dataSourceJdbcRuntimeConfig.foregroundValidationInterval.isPresent()) {
+ poolConfiguration.idleValidationTimeout(dataSourceJdbcRuntimeConfig.foregroundValidationInterval.get());
+ }
if (dataSourceJdbcRuntimeConfig.validationQuerySql.isPresent()) {
String validationQuery = dataSourceJdbcRuntimeConfig.validationQuerySql.get();
poolConfiguration.connectionValidator(new ConnectionValidator() {
diff --git a/extensions/amazon-alexa/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-alexa/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 94df9bef06f78..609bf3df1dd9c 100644
--- a/extensions/amazon-alexa/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-alexa/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -8,4 +8,4 @@ metadata:
categories:
- "cloud"
guide: "https://quarkus.io/guides/amazon-lambda"
- status: "preview"
+ status: "stable"
diff --git a/extensions/amazon-lambda-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-lambda-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index d26b340b6991f..295b2b80f286c 100644
--- a/extensions/amazon-lambda-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-lambda-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -11,4 +11,4 @@ metadata:
categories:
- "cloud"
guide: "https://quarkus.io/guides/amazon-lambda-http"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-lambda-rest/runtime/src/main/java/io/quarkus/amazon/lambda/http/graal/LambdaContainerHandlerSubstitution.java b/extensions/amazon-lambda-rest/runtime/src/main/java/io/quarkus/amazon/lambda/http/graal/LambdaContainerHandlerSubstitution.java
index e2f2ede57953a..d1c9ffebdd196 100644
--- a/extensions/amazon-lambda-rest/runtime/src/main/java/io/quarkus/amazon/lambda/http/graal/LambdaContainerHandlerSubstitution.java
+++ b/extensions/amazon-lambda-rest/runtime/src/main/java/io/quarkus/amazon/lambda/http/graal/LambdaContainerHandlerSubstitution.java
@@ -5,7 +5,7 @@
import com.oracle.svm.core.annotate.TargetClass;
@TargetClass(LambdaContainerHandler.class)
-public class LambdaContainerHandlerSubstitution {
+public final class LambdaContainerHandlerSubstitution {
// afterburner does not work in native mode, so let's ensure it's never registered
@Substitute
diff --git a/extensions/amazon-lambda-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-lambda-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 8caf7277ca0b6..3a755c8ed49a0 100644
--- a/extensions/amazon-lambda-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-lambda-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -11,4 +11,4 @@ metadata:
categories:
- "cloud"
guide: "https://quarkus.io/guides/amazon-lambda-http"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-lambda-xray/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-lambda-xray/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index b68cb8deb0e6a..64721449014f4 100644
--- a/extensions/amazon-lambda-xray/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-lambda-xray/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -10,4 +10,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-lambda#tracing-with-aws-xray-and-graalvm"
categories:
- "cloud"
- status: "preview"
\ No newline at end of file
+ status: "stable"
\ No newline at end of file
diff --git a/extensions/amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 4e66c6d21a76a..2c4356bfb26d4 100644
--- a/extensions/amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -8,7 +8,7 @@ metadata:
categories:
- "cloud"
guide: "https://quarkus.io/guides/amazon-lambda"
- status: "preview"
+ status: "stable"
codestart:
name: "amazon-lambda"
kind: "example"
diff --git a/extensions/amazon-services/common/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/common/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 4ea5a1d78f630..86c58a06796c5 100644
--- a/extensions/amazon-services/common/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/common/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -8,5 +8,5 @@ metadata:
- "amazon"
categories:
- "data"
- status: "preview"
+ status: "stable"
unlisted: true
\ No newline at end of file
diff --git a/extensions/amazon-services/dynamodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/dynamodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 16a93626af4f8..17da2de4d91bc 100644
--- a/extensions/amazon-services/dynamodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/dynamodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -10,4 +10,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-dynamodb"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/iam/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/iam/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index b23a03f1897e5..b7c67762ea2f0 100644
--- a/extensions/amazon-services/iam/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/iam/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-iam"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/kms/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/kms/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 37f5135cba20b..64694f803b24d 100644
--- a/extensions/amazon-services/kms/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/kms/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-kms"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/s3/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/s3/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 837a6866e3f99..cf0a788472a63 100644
--- a/extensions/amazon-services/s3/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/s3/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-s3"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/ses/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/ses/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index ea0cb367cd76e..5eb9296cd7521 100644
--- a/extensions/amazon-services/ses/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/ses/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-ses"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/sns/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/sns/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 507c0dc7dd321..62842e0b8d177 100644
--- a/extensions/amazon-services/sns/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/sns/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-sns"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/sqs/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/sqs/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index c033c7456bb2d..a2f8ad1d26be9 100644
--- a/extensions/amazon-services/sqs/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/sqs/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-sqs"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/amazon-services/ssm/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/amazon-services/ssm/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 8e077230c651f..77b04c7bebcb6 100644
--- a/extensions/amazon-services/ssm/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/amazon-services/ssm/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -9,4 +9,4 @@ metadata:
guide: "https://quarkus.io/guides/amazon-ssm"
categories:
- "data"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchivePredicateBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchivePredicateBuildItem.java
new file mode 100644
index 0000000000000..26aabbf179746
--- /dev/null
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchivePredicateBuildItem.java
@@ -0,0 +1,25 @@
+package io.quarkus.arc.deployment;
+
+import java.util.function.Predicate;
+
+import io.quarkus.builder.item.MultiBuildItem;
+import io.quarkus.deployment.ApplicationArchive;
+
+/**
+ *
+ * By default, only explict/implicit bean archives (as defined by the spec) are considered during the bean discovery. However,
+ * extensions can register a logic to identify additional bean archives.
+ */
+public final class BeanArchivePredicateBuildItem extends MultiBuildItem {
+
+ private final Predicate predicate;
+
+ public BeanArchivePredicateBuildItem(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ public Predicate getPredicate() {
+ return predicate;
+ }
+
+}
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java
index 2bb662fd5a7b0..8133de96db552 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java
@@ -1,6 +1,11 @@
package io.quarkus.arc.deployment;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import org.jboss.jandex.AnnotationInstance;
@@ -35,12 +40,13 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil
List additionalBeanDefiningAnnotations,
List additionalBeans, List generatedBeans,
LiveReloadBuildItem liveReloadBuildItem, BuildProducer generatedClass,
- CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems)
+ CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems,
+ List beanArchivePredicates)
throws Exception {
// First build an index from application archives
IndexView applicationIndex = buildApplicationIndex(config, applicationArchivesBuildItem,
- additionalBeanDefiningAnnotations, customScopes, excludeDependencyBuildItems);
+ additionalBeanDefiningAnnotations, customScopes, excludeDependencyBuildItems, beanArchivePredicates);
// Then build additional index for beans added by extensions
Indexer additionalBeanIndexer = new Indexer();
@@ -81,7 +87,8 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil
private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem,
List additionalBeanDefiningAnnotations,
- CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems) {
+ CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems,
+ List beanArchivePredicates) {
Set archives = applicationArchivesBuildItem.getAllApplicationArchives();
@@ -116,10 +123,8 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui
continue;
}
IndexView index = archive.getIndex();
- // NOTE: Implicit bean archive without beans.xml contains one or more bean classes with a bean defining annotation and no extension
- if (archive.getChildPath("META-INF/beans.xml") != null || archive.getChildPath("WEB-INF/beans.xml") != null
- || (index.getAllKnownImplementors(DotNames.EXTENSION).isEmpty()
- && containsBeanDefiningAnnotation(index, beanDefiningAnnotations))) {
+ if (isExplicitBeanArchive(archive) || isImplicitBeanArchive(index, beanDefiningAnnotations)
+ || isAdditionalBeanArchive(archive, beanArchivePredicates)) {
indexes.add(index);
}
}
@@ -127,6 +132,26 @@ && containsBeanDefiningAnnotation(index, beanDefiningAnnotations))) {
return CompositeIndex.create(indexes);
}
+ private boolean isExplicitBeanArchive(ApplicationArchive archive) {
+ return archive.getChildPath("META-INF/beans.xml") != null || archive.getChildPath("WEB-INF/beans.xml") != null;
+ }
+
+ private boolean isImplicitBeanArchive(IndexView index, Set beanDefiningAnnotations) {
+ // NOTE: Implicit bean archive without beans.xml contains one or more bean classes with a bean defining annotation and no extension
+ return index.getAllKnownImplementors(DotNames.EXTENSION).isEmpty()
+ && containsBeanDefiningAnnotation(index, beanDefiningAnnotations);
+ }
+
+ private boolean isAdditionalBeanArchive(ApplicationArchive archive,
+ List beanArchivePredicates) {
+ for (BeanArchivePredicateBuildItem p : beanArchivePredicates) {
+ if (p.getPredicate().test(archive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean isApplicationArchiveExcluded(ArcConfig config, List excludeDependencyBuildItems,
ApplicationArchive archive) {
if (archive.getArtifactKey() != null) {
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java
index ee60809ab577f..a0d855f1ce38f 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java
@@ -1,12 +1,17 @@
package io.quarkus.arc.deployment;
+import static io.quarkus.arc.deployment.ConfigClassBuildItem.Type.MAPPING;
+import static io.quarkus.arc.deployment.ConfigClassBuildItem.Type.PROPERTIES;
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
-import static io.quarkus.deployment.configuration.ConfigMappingUtils.CONFIG_MAPPING_NAME;
import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix;
+import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import static org.eclipse.microprofile.config.inject.ConfigProperties.UNCONFIGURED_PREFIX;
import static org.jboss.jandex.AnnotationInstance.create;
import static org.jboss.jandex.AnnotationTarget.Kind.CLASS;
+import static org.jboss.jandex.AnnotationTarget.Kind.FIELD;
+import static org.jboss.jandex.AnnotationTarget.Kind.METHOD_PARAMETER;
import static org.jboss.jandex.AnnotationValue.createStringValue;
import java.util.ArrayList;
@@ -14,6 +19,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -25,8 +31,10 @@
import org.eclipse.microprofile.config.inject.ConfigProperties;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.IndexView;
@@ -46,15 +54,16 @@
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
-import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.ConfigurationBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
-import io.quarkus.deployment.configuration.ConfigMappingUtils;
import io.quarkus.deployment.configuration.definition.RootDefinition;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.runtime.annotations.ConfigPhase;
+import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.ConfigMappingLoader;
+import io.smallrye.config.ConfigMappingMetadata;
import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.inject.ConfigProducer;
@@ -66,6 +75,7 @@ public class ConfigBuildStep {
private static final DotName MP_CONFIG_PROPERTIES_NAME = DotName.createSimple(ConfigProperties.class.getName());
private static final DotName MP_CONFIG_VALUE_NAME = DotName.createSimple(ConfigValue.class.getName());
+ private static final DotName CONFIG_MAPPING_NAME = DotName.createSimple(ConfigMapping.class.getName());
private static final DotName MAP_NAME = DotName.createSimple(Map.class.getName());
private static final DotName SET_NAME = DotName.createSimple(Set.class.getName());
private static final DotName LIST_NAME = DotName.createSimple(List.class.getName());
@@ -242,11 +252,79 @@ void generateConfigClasses(
BuildProducer reflectiveClasses,
BuildProducer configClasses) {
- // TODO - Generation of Mapping interface classes can be done in core because they don't require CDI
- ConfigMappingUtils.generateConfigClasses(combinedIndex, generatedClasses, reflectiveClasses, configClasses,
- CONFIG_MAPPING_NAME);
- ConfigMappingUtils.generateConfigClasses(combinedIndex, generatedClasses, reflectiveClasses, configClasses,
- MP_CONFIG_PROPERTIES_NAME);
+ List mappingAnnotations = new ArrayList<>();
+ mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME));
+ mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(MP_CONFIG_PROPERTIES_NAME));
+
+ for (AnnotationInstance instance : mappingAnnotations) {
+ AnnotationTarget target = instance.target();
+ AnnotationValue annotationPrefix = instance.value("prefix");
+
+ if (target.kind().equals(FIELD)) {
+ if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
+ configClasses.produce(
+ toConfigClassBuildItem(instance, toClass(target.asField().type().name()),
+ annotationPrefix.asString()));
+ continue;
+ }
+ }
+
+ if (target.kind().equals(METHOD_PARAMETER)) {
+ if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
+ ClassType classType = target.asMethodParameter().method().parameters()
+ .get(target.asMethodParameter().position()).asClassType();
+ configClasses
+ .produce(toConfigClassBuildItem(instance, toClass(classType.name()), annotationPrefix.asString()));
+ continue;
+ }
+ }
+
+ if (!target.kind().equals(CLASS)) {
+ continue;
+ }
+
+ Class> configClass = toClass(target.asClass().name());
+ String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse("");
+
+ List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass);
+ Set generatedClassesNames = new HashSet<>();
+ Set mappingsInfo = new HashSet<>();
+ configMappingsMetadata.forEach(mappingMetadata -> {
+ generatedClasses.produce(
+ new GeneratedClassBuildItem(true, mappingMetadata.getClassName(), mappingMetadata.getClassBytes()));
+ reflectiveClasses
+ .produce(ReflectiveClassBuildItem.builder(mappingMetadata.getInterfaceType()).methods(true).build());
+ reflectiveClasses
+ .produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName()).constructors(true).build());
+
+ for (Class> parent : getHierarchy(mappingMetadata.getInterfaceType())) {
+ reflectiveClasses.produce(ReflectiveClassBuildItem.builder(parent).methods(true).build());
+ }
+
+ generatedClassesNames.add(mappingMetadata.getClassName());
+
+ ClassInfo mappingInfo = combinedIndex.getIndex()
+ .getClassByName(DotName.createSimple(mappingMetadata.getInterfaceType().getName()));
+ if (mappingInfo != null) {
+ mappingsInfo.add(mappingInfo);
+ }
+ });
+
+ // Search and register possible classes for implicit Converter methods
+ for (ClassInfo classInfo : mappingsInfo) {
+ for (MethodInfo method : classInfo.methods()) {
+ if (!isHandledByProducers(method.returnType()) &&
+ mappingsInfo.stream()
+ .map(ClassInfo::name)
+ .noneMatch(name -> name.equals(method.returnType().name()))) {
+ reflectiveClasses
+ .produce(new ReflectiveClassBuildItem(true, false, method.returnType().name().toString()));
+ }
+ }
+ }
+
+ configClasses.produce(toConfigClassBuildItem(instance, configClass, generatedClassesNames, prefix));
+ }
}
@BuildStep
@@ -327,6 +405,12 @@ void registerConfigClasses(
configClassWithPrefix -> Stream.of(configClassWithPrefix.getKlass(), configClassWithPrefix.getPrefix())
.collect(toList()));
+ recorder.registerConfigMappings(
+ configClasses.stream()
+ .filter(ConfigClassBuildItem::isMapping)
+ .map(configMapping -> configClassWithPrefix(configMapping.getConfigClass(), configMapping.getPrefix()))
+ .collect(toSet()));
+
recorder.registerConfigProperties(
configClasses.stream()
.filter(ConfigClassBuildItem::isProperties)
@@ -335,6 +419,45 @@ void registerConfigClasses(
.collect(toSet()));
}
+ private static Class> toClass(DotName dotName) {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ return classLoader.loadClass(dotName.toString());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("The class (" + dotName.toString() + ") cannot be created during deployment.", e);
+ }
+ }
+
+ private static ConfigClassBuildItem toConfigClassBuildItem(
+ AnnotationInstance instance,
+ Class> configClass,
+ String prefix) {
+ return toConfigClassBuildItem(instance, configClass, emptySet(), prefix);
+ }
+
+ private static ConfigClassBuildItem toConfigClassBuildItem(
+ AnnotationInstance instance,
+ Class> configClass,
+ Set generatedClasses,
+ String prefix) {
+ if (instance.name().equals(CONFIG_MAPPING_NAME)) {
+ return new ConfigClassBuildItem(configClass, generatedClasses, prefix, MAPPING);
+ } else if (instance.name().equals(MP_CONFIG_PROPERTIES_NAME)) {
+ return new ConfigClassBuildItem(configClass, generatedClasses, prefix, PROPERTIES);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private static List> getHierarchy(Class> mapping) {
+ List> interfaces = new ArrayList<>();
+ for (Class> i : mapping.getInterfaces()) {
+ interfaces.add(i);
+ interfaces.addAll(getHierarchy(i));
+ }
+ return interfaces;
+ }
+
private String getPropertyName(String name, ClassInfo declaringClass) {
StringBuilder builder = new StringBuilder();
if (declaringClass.enclosingClass() == null) {
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigClassBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java
similarity index 94%
rename from core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigClassBuildItem.java
rename to extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java
index fb3e80e17225e..1d011ddbe5824 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigClassBuildItem.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java
@@ -1,4 +1,4 @@
-package io.quarkus.deployment.builditem;
+package io.quarkus.arc.deployment;
import java.util.Set;
@@ -48,6 +48,6 @@ public boolean isProperties() {
public enum Type {
MAPPING,
- PROPERTIES
+ PROPERTIES;
}
}
diff --git a/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 5f6b78609781c..9c1ee15c7c8bb 100644
--- a/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -8,4 +8,4 @@ metadata:
- "image"
categories:
- "cloud"
- status: "preview"
\ No newline at end of file
+ status: "stable"
diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
index a87294e34fabb..d162a7dd8efb5 100644
--- a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
+++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
@@ -28,12 +28,14 @@
import io.grpc.internal.ServerImpl;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
+import io.quarkus.arc.deployment.BeanArchivePredicateBuildItem;
import io.quarkus.arc.deployment.CustomScopeAnnotationsBuildItem;
import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.BuiltinScope;
+import io.quarkus.deployment.ApplicationArchive;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
@@ -218,7 +220,7 @@ private Set gatherBlockingMethods(ClassInfo service) {
AnnotationsTransformerBuildItem transformUserDefinedServices(CombinedIndexBuildItem combinedIndexBuildItem,
CustomScopeAnnotationsBuildItem customScopes) {
// User-defined services usually only declare the @GrpcService qualifier
- // We need to add @GrpcEnableRequestContext and @Singleton if needed
+ // We need to add @GrpcEnableRequestContext and @Singleton if needed
Set userDefinedServices = new HashSet<>();
for (AnnotationInstance annotation : combinedIndexBuildItem.getIndex().getAnnotations(GrpcDotNames.GRPC_SERVICE)) {
if (annotation.target().kind() == Kind.CLASS) {
@@ -393,4 +395,16 @@ void configureMetrics(GrpcBuildTimeConfig configuration, Optional() {
+
+ @Override
+ public boolean test(ApplicationArchive archive) {
+ // Every archive that contains a generated implementor of MutinyBean is considered a bean archive
+ return !archive.getIndex().getKnownDirectImplementors(GrpcDotNames.MUTINY_BEAN).isEmpty();
+ }
+ });
+ }
+
}
diff --git a/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 5605cd44a5031..e1c04e3a8a33d 100644
--- a/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -8,7 +8,7 @@ metadata:
- "web"
- "serialization"
- "reactive"
- status: "experimental"
+ status: "stable"
codestart:
name: "grpc"
languages:
diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
index b854dcada8f55..61048fcf548c7 100644
--- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
+++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
@@ -49,6 +49,7 @@
import io.quarkus.arc.deployment.AutoAddScopeBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.BeanContainerListenerBuildItem;
+import io.quarkus.arc.deployment.ConfigClassBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.BeanInfo;
@@ -62,7 +63,6 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
-import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveContextLocaleResolver.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveContextLocaleResolver.java
index 38366401d34e9..9f4023f718699 100644
--- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveContextLocaleResolver.java
+++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveContextLocaleResolver.java
@@ -18,6 +18,11 @@ public ResteasyReactiveContextLocaleResolver(HttpHeaders headers) {
@Override
protected HttpHeaders getHeaders() {
- return headers;
+ try {
+ headers.getLength(); // this forces the creation of the actual object which will fail if there is no request in flight
+ return headers;
+ } catch (IllegalStateException e) {
+ return null;
+ }
}
}
diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java
new file mode 100644
index 0000000000000..3970870f72b53
--- /dev/null
+++ b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java
@@ -0,0 +1,105 @@
+package io.quarkus.jdbc.oracle.deployment;
+
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.nativeimage.ExcludeConfigBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
+
+/**
+ * The Oracle JDBC driver includes a {@literal META-INF/native-image} which enables a set
+ * of global flags we need to control better, so to ensure such flags do not interfere
+ * with requirements of other libraries.
+ *
+ * For this reason, the {@literal META-INF/native-image/native-image.properties} resource
+ * is excluded explicitly; then we re-implement the equivalent directives using Quarkus
+ * build items.
+ *
+ * Other resources such as {@literal jni-config.json} and {@literal resource-config.json}
+ * are not excluded, so to ensure we match the recommendations from the Oracle JDBC
+ * engineering team and make it easier to pick up improvements in these when the driver
+ * gets updated.
+ *
+ * Regarding {@literal reflect-config.json}, we also prefer excluding it for the time
+ * being even though it's strictly not necessary: the reason is that the previous driver
+ * version had a build-breaking mistake; this was fixed in version 21.3 so should no
+ * longer be necessary, but the previous driver had been tested more widely and would
+ * require it, so this would facilitate the option to revert to the older version in
+ * case of problems.
+ */
+public final class OracleMetadataOverrides {
+
+ static final String DRIVER_JAR_MATCH_REGEX = ".*com\\.oracle\\.database\\.jdbc.*";
+ static final String NATIVE_IMAGE_RESOURCE_MATCH_REGEX = "/META-INF/native-image/(?:native-image\\.properties|reflect-config\\.json)";
+
+ /**
+ * Should match the contents of {@literal reflect-config.json}
+ *
+ * @param reflectiveClass builItem producer
+ */
+ @BuildStep
+ void build(BuildProducer reflectiveClass) {
+ //This is to match the Oracle metadata (which we excluded so that we can apply fixes):
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false, "oracle.jdbc.internal.ACProxyable"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.jdbc.driver.T4CDriverExtension"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.jdbc.driver.T2CDriverExtension"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.jdbc.driver.ShardingDriverExtension"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.net.ano.Ano"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.net.ano.AuthenticationService"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.net.ano.DataIntegrityService"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.net.ano.EncryptionService"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.net.ano.SupervisorService"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.jdbc.driver.Message11"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, true, "oracle.sql.TypeDescriptor"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.sql.TypeDescriptorFactory"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, "oracle.sql.AnyDataFactory"));
+ reflectiveClass
+ .produce(new ReflectiveClassBuildItem(true, false, false, "com.sun.rowset.providers.RIOptimisticProvider"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false, "oracle.jdbc.logging.annotations.Supports"));
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false, "oracle.jdbc.logging.annotations.Feature"));
+ }
+
+ @BuildStep
+ void runtimeInitializeDriver(BuildProducer runtimeInitialized) {
+ //These re-implement all the "--initialize-at-build-time" arguments found in the native-image.properties :
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.OracleDriver"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.OracleDriver"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("java.sql.DriverManager"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.LogicalConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.pool.OraclePooledConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.pool.OracleDataSource"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.datasource.impl.OracleDataSource"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.pool.OracleOCIConnectionPool"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.OracleTimeoutThreadPerVM"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TimeoutInterruptHandler"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.HAManager"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.Clock"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer$LazyHolder"));
+ runtimeInitialized
+ .produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem(
+ "oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource$BlockReleaser"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.xa.client.OracleXADataSource"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.replay.OracleXADataSourceImpl"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.datasource.OracleXAConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.xa.client.OracleXAConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.xa.client.OracleXAHeteroConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.T4CXAConnection"));
+ runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.security.o5logon.O5Logon"));
+ }
+
+ @BuildStep
+ ExcludeConfigBuildItem excludeOracleDirectives() {
+ // Excludes both native-image.properties and reflect-config.json, which are reimplemented above
+ return new ExcludeConfigBuildItem(DRIVER_JAR_MATCH_REGEX, NATIVE_IMAGE_RESOURCE_MATCH_REGEX);
+ }
+
+ @BuildStep
+ NativeImageAllowIncompleteClasspathBuildItem naughtyDriver() {
+ return new NativeImageAllowIncompleteClasspathBuildItem("quarkus-jdbc-oracle");
+ }
+
+}
diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleReflections.java b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleReflections.java
index 53854430001ff..118a24d4c1fdd 100644
--- a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleReflections.java
+++ b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleReflections.java
@@ -3,7 +3,6 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
-import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
/**
* Registers the {@code oracle.jdbc.driver.OracleDriver} so that it can be loaded
@@ -24,22 +23,4 @@ void build(BuildProducer reflectiveClass) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, driverName));
}
- @BuildStep
- void runtimeInitializeDriver(BuildProducer runtimeInitialized) {
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.OracleDriver"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.SQLUtil$XMLFactory"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.NamedTypeAccessor$XMLFactory"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.OracleTimeoutThreadPerVM"));
- runtimeInitialized
- .produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.T4CTTIoauthenticate"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer$LazyHolder"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.security.o5logon.O5Logon"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem(
- "oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource$BlockReleaser"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TimeoutInterruptHandler"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.Clock"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.NoSupportHAManager"));
- runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.LogicalConnection"));
- }
}
diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/test/java/io/quarkus/jdbc/oracle/deployment/RegexMatchTest.java b/extensions/jdbc/jdbc-oracle/deployment/src/test/java/io/quarkus/jdbc/oracle/deployment/RegexMatchTest.java
new file mode 100644
index 0000000000000..5a66b4d51b26a
--- /dev/null
+++ b/extensions/jdbc/jdbc-oracle/deployment/src/test/java/io/quarkus/jdbc/oracle/deployment/RegexMatchTest.java
@@ -0,0 +1,45 @@
+package io.quarkus.jdbc.oracle.deployment;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.jupiter.api.Test;
+
+import io.smallrye.common.constraint.Assert;
+
+/**
+ * The metadata override facility of GraalVM's native-image
+ * works with regular expressions.
+ * We're testing our expressions here to match against the
+ * constants the compiler is expecting (inferred by debugging
+ * the compiler) as it's otherwise a bit tricky to assert
+ * if they have been applied.
+ */
+public class RegexMatchTest {
+
+ @Test
+ public void jarRegexIsMatching() {
+ final String EXAMPLE_CLASSPATH = "/home/sanne/sources/quarkus/integration-tests/jpa-oracle/target/quarkus-integration-test-jpa-oracle-999-SNAPSHOT-native-image-source-jar/lib/com.oracle.database.jdbc.ojdbc11-21.3.0.0.jar";
+ final Pattern pattern = Pattern.compile(OracleMetadataOverrides.DRIVER_JAR_MATCH_REGEX);
+ final Matcher matcher = pattern.matcher(EXAMPLE_CLASSPATH);
+ Assert.assertTrue(matcher.find());
+ }
+
+ @Test
+ public void resourceRegexIsMatching() {
+ //We need to exclude both of these:
+ final String RES1 = "/META-INF/native-image/native-image.properties";
+ final String RES2 = "/META-INF/native-image/reflect-config.json";
+ final Pattern pattern = Pattern.compile(OracleMetadataOverrides.NATIVE_IMAGE_RESOURCE_MATCH_REGEX);
+
+ Assert.assertTrue(pattern.matcher(RES1).find());
+ Assert.assertTrue(pattern.matcher(RES2).find());
+
+ //While this one should NOT be ignored:
+ final String RES3 = "/META-INF/native-image/resource-config.json";
+ final String RES4 = "/META-INF/native-image/jni-config.json";
+ Assert.assertFalse(pattern.matcher(RES3).find());
+ Assert.assertFalse(pattern.matcher(RES4).find());
+ }
+
+}
diff --git a/extensions/jdbc/jdbc-oracle/runtime/pom.xml b/extensions/jdbc/jdbc-oracle/runtime/pom.xml
index 9d56502238fd0..88044be2b5ec5 100644
--- a/extensions/jdbc/jdbc-oracle/runtime/pom.xml
+++ b/extensions/jdbc/jdbc-oracle/runtime/pom.xml
@@ -24,7 +24,7 @@
com.oracle.database.jdbc
- ojdbc8
+ ojdbc11org.graalvm.nativeimage
diff --git a/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHandler.java b/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHandler.java
index ed127a722a83b..58f3eff0b634e 100644
--- a/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHandler.java
+++ b/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHandler.java
@@ -12,11 +12,11 @@ public static void handle(Exception e) {
if (e.getCause() instanceof SSLHandshakeException) {
LOG.error(
"The application could not be deployed to the cluster because the Kubernetes API Server certificates are not trusted. The certificates can be configured using the relevant configuration properties under the 'quarkus.kubernetes-client' config root, or \"quarkus.kubernetes-client.trust-certs=true\" can be set to explicitly trust the certificates (not recommended)");
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else {
- throw new RuntimeException(e);
- }
+ }
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else {
+ throw new RuntimeException(e);
}
}
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
index c358fb29ea50d..362b63ae06357 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
@@ -11,11 +11,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -25,6 +26,7 @@
import io.dekorate.utils.Serialization;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
+import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.openshift.api.model.Route;
@@ -42,7 +44,6 @@
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.kubernetes.client.deployment.KubernetesClientErrorHandler;
import io.quarkus.kubernetes.client.spi.KubernetesClientBuildItem;
-import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem;
public class KubernetesDeployer {
@@ -60,7 +61,7 @@ public void selectDeploymentTarget(ContainerImageInfoBuildItem containerImageInf
Optional activeContainerImageCapability = ContainerImageCapabilitiesUtil
.getActiveContainerImageCapability(capabilities);
- if (!activeContainerImageCapability.isPresent()) {
+ if (activeContainerImageCapability.isEmpty()) {
// we can't thrown an exception here, because it could prevent the Kubernetes resources from being generated
return;
}
@@ -85,10 +86,10 @@ public void deploy(KubernetesClientBuildItem kubernetesClient,
return;
}
- if (!selectedDeploymentTarget.isPresent()) {
+ if (selectedDeploymentTarget.isEmpty()) {
- if (!ContainerImageCapabilitiesUtil
- .getActiveContainerImageCapability(capabilities).isPresent()) {
+ if (ContainerImageCapabilitiesUtil
+ .getActiveContainerImageCapability(capabilities).isEmpty()) {
throw new RuntimeException(
"A Kubernetes deployment was requested but no extension was found to build a container image. Consider adding one of following extensions: "
+ CONTAINER_IMAGE_EXTENSIONS_STR + ".");
@@ -158,11 +159,6 @@ private DeploymentTargetEntry determineDeploymentTarget(
return selectedTarget;
}
- private Optional getOptionalDeploymentTarget(
- List deploymentTargets, String name) {
- return deploymentTargets.stream().filter(d -> name.equals(d.getName())).findFirst();
- }
-
private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget,
KubernetesClient client, Path outputDir,
OpenshiftConfig openshiftConfig, ApplicationInfoBuildItem applicationInfo) {
@@ -174,12 +170,13 @@ private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget,
try (FileInputStream fis = new FileInputStream(manifest)) {
KubernetesList list = Serialization.unmarshalAsList(fis);
- distinct(list.getItems()).forEach(i -> {
- if (KNATIVE.equals(deploymentTarget.getName().toLowerCase())) {
- client.resource(i).inNamespace(namespace).deletingExisting().createOrReplace();
- } else {
- client.resource(i).inNamespace(namespace).createOrReplace();
+ list.getItems().stream().filter(distinctByResourceKey()).forEach(i -> {
+ final var r = client.resource(i).inNamespace(namespace);
+ if (shouldDeleteExisting(deploymentTarget, i)) {
+ r.delete();
+ r.waitUntilCondition(Objects::isNull, 10, TimeUnit.SECONDS);
}
+ r.createOrReplace();
log.info("Applied: " + i.getKind() + " " + i.getMetadata().getName() + ".");
});
@@ -221,13 +218,15 @@ private void printExposeInformation(KubernetesClient client, KubernetesList list
}
}
- public static Predicate distictByResourceKey() {
+ private static boolean shouldDeleteExisting(DeploymentTargetEntry deploymentTarget, HasMetadata resource) {
+ return KNATIVE.equalsIgnoreCase(deploymentTarget.getName())
+ || resource instanceof Service
+ || (Objects.equals("v1", resource.getApiVersion()) && Objects.equals("Service", resource.getKind()));
+ }
+
+ private static Predicate distinctByResourceKey() {
Map