diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7e705b628da6ae..43c8652b434de8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,9 @@ updates: - dependency-name: org.yaml:snakeyaml - dependency-name: com.google.guava:guava - dependency-name: com.vackosar.gitflowincrementalbuilder:gitflow-incremental-builder + - dependency-name: com.jcraft:jzlib + - dependency-name: org.jboss.logging:* + - dependency-name: org.jboss.logmanager:* # Quarkus - dependency-name: io.quarkus.gizmo:gizmo - dependency-name: io.quarkus.http:* @@ -34,7 +37,8 @@ updates: - dependency-name: org.postgresql:postgresql - dependency-name: org.mariadb.jdbc:mariadb-java-client - dependency-name: mysql:mysql-connector-java - - dependency-name: org.apache.derby:derbyclient + - dependency-name: org.apache.derby:* + - dependency-name: com.ibm.db2:jcc # Kafka - dependency-name: org.apache.kafka:* - dependency-name: org.apache.zookeeper:zookeeper @@ -94,13 +98,18 @@ updates: - dependency-name: com.github.ben-manes.caffeine:caffeine # Jackson - dependency-name: com.fasterxml.jackson:jackson-bom + - dependency-name: com.fasterxml:classmate # AWS - dependency-name: software.amazon.awssdk:* - dependency-name: com.amazon.alexa:* - dependency-name: com.amazonaws:* + - dependency-name: com.amazonaws.serverless:* + # Azure + - dependency-name: com.microsoft.azure.functions:* # Apache Commons - dependency-name: commons-io:commons-io - dependency-name: org.apache.commons:commons-lang3 + - dependency-name: commons-codec:commons-codec # Micrometer - dependency-name: io.micrometer:micrometer-bom # BouncyCastle @@ -108,6 +117,12 @@ updates: # Logstash - dependency-name: biz.paluch.logging:logstash-gelf - dependency-name: io.sentry:* + # MongoDB + - dependency-name: org.mongodb:* + # Avro + - dependency-name: org.apache.avro:* + # gRPC + - dependency-name: io.grpc:* rebase-strategy: disabled - package-ecosystem: gradle directory: "/integration-tests/gradle" diff --git a/.github/native-tests.json b/.github/native-tests.json index 542c5acc3e5a39..7153fbe891e5e0 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -110,7 +110,7 @@ { "category": "Misc4", "timeout": 65, - "test-modules": "smallrye-config smallrye-graphql picocli-native gradle micrometer-mp-metrics micrometer-prometheus smallrye-metrics logging-json jaxp" + "test-modules": "smallrye-config smallrye-graphql picocli-native gradle micrometer-mp-metrics micrometer-prometheus smallrye-metrics logging-json jaxp opentelemetry" }, { "category": "Spring", diff --git a/.github/quarkus-bot.yml b/.github/quarkus-bot.yml index 2eda1651cb9be5..20a7345632d8db 100644 --- a/.github/quarkus-bot.yml +++ b/.github/quarkus-bot.yml @@ -290,8 +290,10 @@ triage: - extensions/cache/ - integration-tests/cache/ - labels: [area/cli] + title: "cli |qs " directories: - devtools/cli/ + notify: [maxandersen,ebullient,gsmet] - labels: [area/config] directories: - extensions/config-yaml/ @@ -437,3 +439,5 @@ triage: - labels: [kind/extension-proposal] title: quarkiverse notify: [gastaldi, gsmet, aloubyansky] + qe: + notify: [rsvoboda, mjurc] diff --git a/.github/workflows/jdk-early-access-build.yml b/.github/workflows/jdk-early-access-build.yml new file mode 100644 index 00000000000000..97e87aa4ffe58f --- /dev/null +++ b/.github/workflows/jdk-early-access-build.yml @@ -0,0 +1,115 @@ +name: Quarkus Early Access JDK Build + +on: + schedule: + # midnight every weekday + saturday + - cron: '0 0 * * 1-6' + workflow_dispatch: + inputs: + jdkVersion: + description: 'JDK version' + required: true + # switch to 17-ea once 16 has been fixed and is running in regular CI + # make sure to keep the default of JDK_VERSION in sync! + default: '16' + +env: + JDK_VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.jdkVersion || '16' }} + # Workaround testsuite locale issue + LANG: en_US.UTF-8 + JVM_TEST_MAVEN_OPTS: "-e -B --settings .github/mvn-settings.xml -Dtest-containers -Dstart-containers -Dformat.skip" + DB_USER: hibernate_orm_test + DB_PASSWORD: hibernate_orm_test + DB_NAME: hibernate_orm_test +jobs: + linux-jvm-test: + name: "JVM Tests - Early Access JDK Build" + runs-on: ubuntu-latest + if: "github.repository == 'quarkusio/quarkus' || github.event_name == 'workflow_dispatch'" + timeout-minutes: 360 + env: + MAVEN_OPTS: -Xmx2048m -XX:MaxMetaspaceSize=1000m + steps: + + - name: Set up JDK + # AdoptOpenJDK does not seem to provide EA builds, so use the standard action which provides a Zulu JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ env.JDK_VERSION }} + - name: Print Java version + shell: bash + run: java -version + + - name: Stop mysql + shell: bash + run: | + ss -ln + sudo service mysql stop || true + - uses: actions/checkout@v2 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && github.event.ref || 'main' }} + - uses: n1hility/cancel-previous-runs@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + workflow: jdk-early-access-build.yml + - name: Reclaim Disk Space + run: .github/ci-prerequisites.sh + - name: Get Date + id: get-date + run: | + echo "::set-output name=date::$(/bin/date -u "+%Y-%m")" + shell: bash + - name: Cache Maven Repository + id: cache-maven + uses: actions/cache@v2 + with: + path: ~/.m2/repository + # refresh cache every month to avoid unlimited growth + # also add JDK version to key to avoid poisioning of the CI cache + key: q2maven-${{ steps.get-date.outputs.date }}-${{ env.JDK_VERSION }} + - name: Build with Maven + # try to gather as many failures as possible, test reports are evaluated in subsequent actions + run: | + mvn $JVM_TEST_MAVEN_OPTS -Dtcks install -fae -Dmaven.test.failure.ignore=true + - name: Publish Test Report + if: always() + uses: scacap/action-surefire-report@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # fail required to upload failure archive in subsequent actions + fail_on_test_failures: true + - name: Publish Gradle Test Report + if: always() + uses: scacap/action-surefire-report@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # fail required to upload failure archive in subsequent actions + fail_on_test_failures: true + check_name: 'Test Report for Gradle' + report_paths: '**/build/test-results/test/TEST-*.xml' + - name: Prepare failure archive (if maven failed) + if: failure() + shell: bash + run: find . -name '*-reports' -type d -o -name '*.log' | tar -czf test-reports.tgz -T - + - name: Upload failure Archive (if maven failed) + uses: actions/upload-artifact@v1 + if: failure() + with: + name: test-reports-linux-jvm${{ env.JDK_VERSION }} + path: 'test-reports.tgz' + - name: Delete Local Artifacts From Cache + if: always() + shell: bash + run: rm -r ~/.m2/repository/io/quarkus + - name: Report status + uses: jbangdev/jbang-action@v0.69.1 + if: "always() && github.repository == 'quarkusio/quarkus' && github.event_name != 'workflow_dispatch'" + with: + script: .github/NativeBuildReport.java + scriptargs: | + issueNumber=15867 + runId=${{ github.run_id }} + status=${{ job.status }} + token=${{ secrets.GITHUB_API_TOKEN }} + issueRepo=${{ github.repository }} + thisRepo=${{ github.repository }} diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 0f3fe0a7e04410..265e5392c3df1e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -27,10 +27,11 @@ 0.0.12 0.1.15 0.1.5 + 1.0.1 + 1.0.1-alpha 0.34.3 3.1.0.Beta2 1.0.0.Final - 1.4.3 1.6.5 0.22.0 1.4 @@ -40,18 +41,18 @@ 1.0.1 1.4.1 1.1.1 - 1.5.0 - 1.11.1 + 1.6.0 + 1.12.0 2.2.6 2.4.6 2.0.26 - 1.0.22 + 1.0.26 1.3.5 4.3.2 2.4.4 1.1.0 1.0.13 - 1.4.0 + 1.5.0 2.9.0 1.2.1 1.3.5 @@ -80,25 +81,25 @@ 3.6.3 1.4.1 3.0-alpha-2 - 30.1-jre + 30.1.1-jre 3.2.1 3.6.0 2.1.0 21.0.0 1.0.7.Final - 2.12.1 + 2.12.2 1.0.0.Final 3.12.0 1.14 - 1.3.4 - 5.4.29.Final + 1.5.1 + 5.4.30.Final 1.0.0.CR1 6.2.0.Final 6.0.2.Final 5.10.6.Final 1.1.1.Final - 1.9 + 1.10 7.6.0.Final 7.10.0 1.3.8 @@ -109,10 +110,10 @@ 1.1.0.Final 1.5.4.Final-format-001 1.0.1.Final - 1.15.1.Final + 1.15.3.Final 1.8.7.Final 3.2.0.Final - 3.9.5 + 3.9.6 4.5.13 4.4.14 4.1.4 @@ -138,7 +139,6 @@ 1.0.3 3.4.1.Final 0.14.0 - 1.5.0 2.7.0 3.5.7 @@ -146,28 +146,28 @@ 1.24.1 1.4 1.2.1 - 3.7.0 + 3.8.0 1.3.1 2.8.0 - 2.16.19 + 2.16.29 2.37.1 1.4.0 - 1.4.31 - 0.14.2 + 1.4.32 + 2.1.1 0.10.0 3.0.1 4.0.3 1.6.5 - 1.0.6 + 1.0.9 5.11.0.202103091610-r - 7.7.0 - 1.0.8 - 4.3.1 + 7.7.1 + 1.0.9 + 4.3.2 1.28 6.0.0 - 4.2.3 - 4.1.0 - 1.0.1 + 4.2.4 + 4.2.2 + 1.2.0 2.17.0 0.33.8 3.14.9 @@ -185,10 +185,10 @@ 5.3.1 4.8 1.1.4.Final - 12.0.3 + 12.0.4 1.14.1 0.1.55 - 1.1.1 + 1.1.3 2.5.2 2.2.0 0.17.0 @@ -198,7 +198,7 @@ 1.34.0 1.0.1 3.14.0 - 3.13.0 + ${protobuf-java.version} 4.6.1 0.3.4 @@ -208,7 +208,7 @@ 0.46 1.0.0.Beta1 1.2.0.Final - 1.10.0 + 1.10.2 0.8.6 1.15.2 @@ -332,6 +332,23 @@ pom import + + + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import + + + io.opentelemetry + opentelemetry-bom-alpha + ${opentelemetry-alpha.version} + pom + import + + @@ -667,6 +684,11 @@ quarkus-oidc ${project.version} + + io.quarkus + quarkus-test-oidc-server + ${project.version} + io.quarkus quarkus-oidc-deployment @@ -1913,6 +1935,16 @@ quarkus-rest-client-reactive-deployment ${project.version} + + io.quarkus + quarkus-rest-client-reactive-jackson + ${project.version} + + + io.quarkus + quarkus-rest-client-reactive-jackson-deployment + ${project.version} + io.quarkus quarkus-resteasy-reactive-qute @@ -1943,6 +1975,26 @@ quarkus-resteasy-reactive-jackson-deployment ${project.version} + + io.quarkus + quarkus-resteasy-reactive-jackson-common + ${project.version} + + + io.quarkus + quarkus-resteasy-reactive-jackson-common-deployment + ${project.version} + + + io.quarkus + quarkus-resteasy-reactive-links + ${project.version} + + + io.quarkus + quarkus-resteasy-reactive-links-deployment + ${project.version} + io.quarkus quarkus-reactive-datasource @@ -2085,7 +2137,7 @@ io.quarkus - quarkus-kubernetes-client-deployment-internal + quarkus-kubernetes-client-internal-deployment ${project.version} @@ -2313,6 +2365,26 @@ quarkus-micrometer-registry-prometheus ${project.version} + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + + + io.quarkus + quarkus-opentelemetry + ${project.version} + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger-deployment + ${project.version} + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger + ${project.version} + @@ -4189,6 +4261,11 @@ mockito-core ${mockito.version} + + org.mockito + mockito-inline + ${mockito.version} + org.mockito mockito-junit-jupiter @@ -4233,7 +4310,7 @@ io.smallrye.reactive smallrye-mutiny-vertx-core - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} com.fasterxml.jackson.core @@ -4248,27 +4325,27 @@ io.smallrye.reactive smallrye-mutiny-vertx-web-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-mutiny-vertx-sql-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-mutiny-vertx-db2-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-mutiny-vertx-mysql-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-mutiny-vertx-pg-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} @@ -4279,12 +4356,12 @@ io.smallrye.reactive smallrye-mutiny-vertx-mail-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-mutiny-vertx-redis-client - ${mutiny-vertx.version} + ${smallrye-reactive-utils.version} io.vertx @@ -4538,17 +4615,17 @@ io.smallrye.reactive smallrye-reactive-converter-api - ${smallrye-converter-api.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-reactive-converter-mutiny - ${smallrye-converter-api.version} + ${smallrye-reactive-utils.version} io.smallrye.reactive smallrye-reactive-converter-rxjava2 - ${smallrye-converter-api.version} + ${smallrye-reactive-utils.version} @@ -5084,6 +5161,34 @@ quarkus-devtools-registry-client ${project.version} + + io.quarkus + quarkus-apache-httpclient + ${project.version} + + + io.quarkus + quarkus-apache-httpclient-deployment + ${project.version} + + + + + + io.quarkus + quarkus-platform-bom-maven-plugin + 0.0.8 + + + process-resources + + flatten-platform-bom + + + + + + diff --git a/build-parent/pom.xml b/build-parent/pom.xml index ae17f52c265806..a8784558ca9ac2 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -24,7 +24,7 @@ 3.8.1 - 1.4.31 + 1.4.32 2.12.13 4.4.0 @@ -92,7 +92,7 @@ - quay.io/keycloak/keycloak:12.0.3 + quay.io/keycloak/keycloak:12.0.4 4.0.13 @@ -180,7 +180,13 @@ io.quarkus - quarkus-platform-descriptor-json + quarkus-project-core-extension-codestarts + ${project.version} + + + io.quarkus + quarkus-project-core-extension-codestarts + pom ${project.version} @@ -536,6 +542,8 @@ + + .cache eclipse-format.xml LF ${format.skip} @@ -546,6 +554,8 @@ impsort-maven-plugin 1.6.0 + + .cache java.,javax.,org.,com. * ${format.skip} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index c127fe43503a16..85efde8c45ce2d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -58,6 +58,7 @@ public enum Capability { HIBERNATE_ENVERS, HIBERNATE_REACTIVE, HIBERNATE_VALIDATOR, + OPENTELEMETRY_TRACER, /** * Presence of an io.opentracing tracer (for example, Jaeger). */ diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java index b953e83109000d..e30ef0bb77037c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java @@ -73,6 +73,8 @@ public enum Feature { OIDC_CLIENT_FILTER, OIDC_TOKEN_PROPAGATION, OPENSHIFT_CLIENT, + OPENTELEMETRY, + OPENTELEMETRY_JAEGER_EXPORTER, PICOCLI, QUARTZ, QUTE, @@ -92,6 +94,7 @@ public enum Feature { RESTEASY_REACTIVE_JAXRS_CLIENT, RESTEASY_REACTIVE_JSONB, RESTEASY_REACTIVE_JACKSON, + RESTEASY_REACTIVE_LINKS, REST_CLIENT, REST_CLIENT_JACKSON, REST_CLIENT_JAXB, diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ExcludeDependencyBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ExcludeDependencyBuildItem.java new file mode 100644 index 00000000000000..646a9d7252272f --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ExcludeDependencyBuildItem.java @@ -0,0 +1,37 @@ +package io.quarkus.deployment.builditem; + +import java.util.Optional; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * Build item that defines dependencies that should not be indexed. This can be used when a dependency contains + * a marker file (e.g. META-INF/beans.xml). + */ +public final class ExcludeDependencyBuildItem extends MultiBuildItem { + private final String groupId; + private final String artifactId; + private final Optional classifier; + + public ExcludeDependencyBuildItem(String groupId, String artifactId) { + this(groupId, artifactId, Optional.empty()); + } + + public ExcludeDependencyBuildItem(String groupId, String artifactId, Optional classifier) { + this.groupId = groupId; + this.artifactId = artifactId; + this.classifier = classifier; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public Optional getClassifier() { + return classifier; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java index 48572a88c31c78..109885691ee38d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java @@ -296,7 +296,7 @@ ReadResult run() { nameBuilder.setLength(len); } // sweep-up - for (String propertyName : getAllProperties()) { + for (String propertyName : config.getPropertyNames()) { if (propertyName.equals(ConfigSource.CONFIG_ORDINAL)) { continue; } @@ -718,19 +718,6 @@ private Converter getConverter(SmallRyeConfig config, Field field, ConverterT convByType.put(valueType, converter); return converter; } - - /** - * We collect all properties from ConfigSources, because Config#getPropertyNames exclude the active profiled - * properties, meaning that the property is written in the default config source unprofiled. This may cause - * issues if we run with a different profile and fallback to defaults. - */ - private Set getAllProperties() { - Set properties = new HashSet<>(); - for (ConfigSource configSource : config.getConfigSources()) { - properties.addAll(configSource.getPropertyNames()); - } - return properties; - } } public static final class ReadResult { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/ClassComparisonUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/ClassComparisonUtil.java index afb543f8dc541d..81850cefc40b57 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/ClassComparisonUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/ClassComparisonUtil.java @@ -52,9 +52,14 @@ static boolean isSameStructure(ClassInfo clazz, ClassInfo old) { return false; } } - for (MethodInfo method : clazz.methods()) { + List methods = clazz.methods(); + List oldMethods = old.methods(); + if (methods.size() != oldMethods.size()) { + return false; + } + for (MethodInfo method : methods) { MethodInfo om = null; - for (MethodInfo i : old.methods()) { + for (MethodInfo i : oldMethods) { if (!i.name().equals(method.name())) { continue; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java index b30ac9fac979a2..92b052932c2022 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java @@ -279,8 +279,11 @@ protected QuarkusDevModeLauncher() { */ protected void prepare() throws Exception { - // the following flags reduce startup time and are acceptable only for dev purposes - args.add("-XX:TieredStopAtLevel=1"); + if (!JavaVersionUtil.isGraalvmJdk()) { + // prevent C2 compiler for kicking in - makes startup a little faster + // it only makes sense in dev-mode but it is not available when GraalVM is used as the JDK + args.add("-XX:TieredStopAtLevel=1"); + } if (suspend != null) { switch (suspend.toLowerCase(Locale.ENGLISH)) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index 6643cf3963bc38..bca96583a9881e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -10,9 +10,14 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.instrument.ClassDefinition; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collections; @@ -194,9 +199,7 @@ public boolean doScan(boolean userInitiated) throws IOException { ClassScanResult changedClassResults = checkForChangedClasses(); Set filesChanged = checkForFileChange(); - boolean configFileRestartNeeded = filesChanged.stream().map(watchedFilePaths::get).anyMatch(Boolean.TRUE::equals); - boolean instrumentationChange = false; if (ClassChangeAgent.getInstrumentation() != null && lastStartIndex != null && !configFileRestartNeeded && devModeType != DevModeType.REMOTE_LOCAL_SIDE) { @@ -616,7 +619,7 @@ public RuntimeUpdatesProcessor setDisableInstrumentationForIndexPredicate( public RuntimeUpdatesProcessor setWatchedFilePaths(Map watchedFilePaths) { this.watchedFilePaths = watchedFilePaths; watchedFileTimestamps.clear(); - + Map extraWatchedFilePaths = new HashMap<>(); for (DevModeContext.ModuleInfo module : context.getAllModules()) { String rootPath = module.getResourcePath(); @@ -638,9 +641,15 @@ public RuntimeUpdatesProcessor setWatchedFilePaths(Map watchedF } } else { watchedFileTimestamps.put(config, 0L); + Map extraWatchedFileTimestamps = expandGlobPattern(root, config); + watchedFileTimestamps.putAll(extraWatchedFileTimestamps); + for (Path extraPath : extraWatchedFileTimestamps.keySet()) { + extraWatchedFilePaths.put(root.relativize(extraPath).toString(), this.watchedFilePaths.get(path)); + } } } } + this.watchedFilePaths.putAll(extraWatchedFilePaths); return this; } @@ -666,4 +675,28 @@ public void close() throws IOException { FSWatchUtil.shutdown(); } + private Map expandGlobPattern(Path root, Path configFile) { + PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + configFile.toString()); + Map files = new HashMap<>(); + try { + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (pathMatcher.matches(file)) { + files.put(file, attrs.lastModifiedTime().toMillis()); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return files; + } + } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ide/IdeProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/ide/IdeProcessor.java index aedf997e0e3c72..d27b98c7ddaf83 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/ide/IdeProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/ide/IdeProcessor.java @@ -17,6 +17,8 @@ import java.util.function.Predicate; import java.util.stream.Stream; +import org.jboss.logging.Logger; + import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; @@ -25,6 +27,8 @@ public class IdeProcessor { + private static final Logger log = Logger.getLogger(IdeProcessor.class); + private static Map> IDE_MARKER_FILES = new HashMap<>(); private static Map, Ide> IDE_PROCESSES = new HashMap<>(); private static Map> IDE_ARGUMENTS_EXEC_INDICATOR = new HashMap<>(); @@ -135,7 +139,13 @@ public IdeRunningProcessBuildItem detectRunningIdeProcesses(LaunchModeBuildItem return null; } Set result = new HashSet<>(4); - List processInfos = ProcessUtil.runningProcesses(); + List processInfos = Collections.emptyList(); + try { + processInfos = ProcessUtil.runningProcesses(); + } catch (Exception e) { + // this shouldn't be a terminal failure, so just log it to the console + log.warn(e.getMessage()); + } for (ProcessInfo processInfo : processInfos) { for (Map.Entry, Ide> entry : IDE_PROCESSES.entrySet()) { if (entry.getKey().test(processInfo)) { @@ -193,7 +203,7 @@ public static List runningProcesses() { }); return result; } catch (Exception e) { - throw new RuntimeException("Unable to determine running processes", e); + throw new RuntimeException("Unable to determine running IDE processes", e); } } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java index 3cb12a9ffb3f41..dc1f22ebaee2fc 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java @@ -35,6 +35,7 @@ import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.builditem.nativeimage.UnsafeAccessedFieldBuildItem; +import io.quarkus.gizmo.AssignableResultHandle; import io.quarkus.gizmo.CatchBlockCreator; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; @@ -66,7 +67,8 @@ public class NativeImageAutoFeatureStep { static final String JNI_RUNTIME_ACCESS = "com.oracle.svm.core.jni.JNIRuntimeAccess"; static final String BEFORE_ANALYSIS_ACCESS = Feature.BeforeAnalysisAccess.class.getName(); static final String DYNAMIC_PROXY_REGISTRY = "com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry"; - static final String LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.LocalizationFeature"; + static final String LEGACY_LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.LocalizationFeature"; + static final String LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.localization.LocalizationFeature"; @BuildStep void generateFeature(BuildProducer nativeImageClass, @@ -217,12 +219,28 @@ public void write(String s, byte[] bytes) { } if (!resourceBundles.isEmpty()) { - ResultHandle locClass = overallCatch.loadClass(LOCALIZATION_FEATURE); + AssignableResultHandle registerMethod = overallCatch.createVariable(Method.class); + AssignableResultHandle locClass = overallCatch.createVariable(Class.class); + TryBlock locTryBlock = overallCatch.tryBlock(); + ResultHandle legacyLocClass = locTryBlock.loadClass(LEGACY_LOCALIZATION_FEATURE); + locTryBlock.assign(locClass, legacyLocClass); + + ResultHandle legacyParams = locTryBlock.marshalAsArray(Class.class, locTryBlock.loadClass(String.class)); + ResultHandle legacyRegisterMethod = locTryBlock.invokeVirtualMethod( + ofMethod(Class.class, "getDeclaredMethod", Method.class, String.class, Class[].class), legacyLocClass, + locTryBlock.load("addBundleToCache"), legacyParams); + locTryBlock.assign(registerMethod, legacyRegisterMethod); + + CatchBlockCreator locCatchBlock = locTryBlock.addCatch(NoClassDefFoundError.class); + ResultHandle newLocClass = locCatchBlock.loadClass(LOCALIZATION_FEATURE); + locCatchBlock.assign(locClass, newLocClass); + + ResultHandle newParams = locCatchBlock.marshalAsArray(Class.class, locCatchBlock.loadClass(String.class)); + ResultHandle newRegisterMethod = locCatchBlock.invokeVirtualMethod( + ofMethod(Class.class, "getDeclaredMethod", Method.class, String.class, Class[].class), newLocClass, + locCatchBlock.load("prepareBundle"), newParams); + locCatchBlock.assign(registerMethod, newRegisterMethod); - ResultHandle params = overallCatch.marshalAsArray(Class.class, overallCatch.loadClass(String.class)); - ResultHandle registerMethod = overallCatch.invokeVirtualMethod( - ofMethod(Class.class, "getDeclaredMethod", Method.class, String.class, Class[].class), locClass, - overallCatch.load("addBundleToCache"), params); overallCatch.invokeVirtualMethod(ofMethod(AccessibleObject.class, "setAccessible", void.class, boolean.class), registerMethod, overallCatch.load(true)); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/WebJarUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/WebJarUtil.java index 18a29e5ef0e54d..94b686923613ac 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/WebJarUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/WebJarUtil.java @@ -38,6 +38,7 @@ import io.quarkus.bootstrap.util.IoUtils; import io.quarkus.builder.Version; import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.runtime.LaunchMode; import io.smallrye.common.io.jar.JarFiles; @@ -99,15 +100,18 @@ && isOverride(userApplication.getPaths(), classLoader, fileName, modulename)) { } } - public static Path copyResourcesForDevOrTest(CurateOutcomeBuildItem curateOutcomeBuildItem, + public static Path copyResourcesForDevOrTest(LiveReloadBuildItem liveReloadBuildItem, + CurateOutcomeBuildItem curateOutcomeBuildItem, LaunchModeBuildItem launchMode, AppArtifact resourcesArtifact, String rootFolderInJar) throws IOException { - return copyResourcesForDevOrTest(curateOutcomeBuildItem, launchMode, resourcesArtifact, rootFolderInJar, true); + return copyResourcesForDevOrTest(liveReloadBuildItem, curateOutcomeBuildItem, launchMode, resourcesArtifact, + rootFolderInJar, true); } - public static Path copyResourcesForDevOrTest(CurateOutcomeBuildItem curateOutcomeBuildItem, + public static Path copyResourcesForDevOrTest(LiveReloadBuildItem liveReloadBuildItem, + CurateOutcomeBuildItem curateOutcomeBuildItem, LaunchModeBuildItem launchMode, AppArtifact resourcesArtifact, String rootFolderInJar, @@ -121,7 +125,9 @@ public static Path copyResourcesForDevOrTest(CurateOutcomeBuildItem curateOutcom // Clean if not in dev mode or if the resources jar is a snapshot version if (!launchMode.getLaunchMode().equals(LaunchMode.DEVELOPMENT) - || resourcesArtifact.getVersion().contains(SNAPSHOT_VERSION)) { + || resourcesArtifact.getVersion().contains(SNAPSHOT_VERSION) + || (launchMode.getLaunchMode().equals(LaunchMode.DEVELOPMENT) && !liveReloadBuildItem.isLiveReload())) { + IoUtils.createOrEmptyDir(deploymentPath); } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/CascadingConditionalDependenciesTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/CascadingConditionalDependenciesTest.java new file mode 100644 index 00000000000000..2ce5655986f9e6 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/CascadingConditionalDependenciesTest.java @@ -0,0 +1,84 @@ +package io.quarkus.deployment.conditionaldeps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppDependency; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class CascadingConditionalDependenciesTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + final TsQuarkusExt extE = new TsQuarkusExt("ext-e"); + final TsQuarkusExt extF = new TsQuarkusExt("ext-f"); + final TsQuarkusExt extG = new TsQuarkusExt("ext-g"); + final TsQuarkusExt extH = new TsQuarkusExt("ext-h"); + + extA.setConditionalDeps(extB, extF); + + extB.setDependencyCondition(extC); + extB.setConditionalDeps(extD); + + extD.setDependencyCondition(extE); + + extE.addDependency(extC); + extE.setDependencyCondition(extA); + extE.setDependencyCondition(extG); + + extF.setDependencyCondition(extD); + + extG.setDependencyCondition(extH); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extB.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + addToExpectedLib(extE.getRuntime()); + addToExpectedLib(extF.getRuntime()); + + install(extA); + install(extB); + install(extC); + install(extD); + install(extE); + install(extF); + install(extG); + install(extH); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extA) + .addDependency(extE); + } + + @Override + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { + final Set expected = new HashSet<>(); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-c-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-a-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-b-deployment", TsArtifact.DEFAULT_VERSION), "runtime")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-d-deployment", TsArtifact.DEFAULT_VERSION), "runtime")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-e-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-f-deployment", TsArtifact.DEFAULT_VERSION), "runtime")); + assertEquals(expected, new HashSet<>(deploymentDeps)); + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithSingleConditionTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithSingleConditionTest.java new file mode 100644 index 00000000000000..6c38fb3c0231b7 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithSingleConditionTest.java @@ -0,0 +1,53 @@ +package io.quarkus.deployment.conditionaldeps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppDependency; +import io.quarkus.bootstrap.model.AppModel; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class ConditionalDependencyWithSingleConditionTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA); + install(extB); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extB); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extB.getRuntime()); + addToExpectedLib(extC.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC) + .addDependency(extA); + } + + @Override + protected void assertAppModel(AppModel appModel) throws Exception { + final List deploymentDeps = appModel.getDeploymentDependencies(); + final Set expected = new HashSet<>(); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-c-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-a-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-b-deployment", TsArtifact.DEFAULT_VERSION), "runtime")); + assertEquals(expected, new HashSet<>(deploymentDeps)); + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithTwoConditionsTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithTwoConditionsTest.java new file mode 100644 index 00000000000000..5f346bb2f7e0f9 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ConditionalDependencyWithTwoConditionsTest.java @@ -0,0 +1,56 @@ +package io.quarkus.deployment.conditionaldeps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppDependency; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class ConditionalDependencyWithTwoConditionsTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA, extD); + install(extB); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extB); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extB.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC) + .addDependency(extA) + .addDependency(extD); + } + + @Override + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { + final Set expected = new HashSet<>(); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-c-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-a-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-b-deployment", TsArtifact.DEFAULT_VERSION), "runtime")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-d-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + assertEquals(expected, new HashSet<>(deploymentDeps)); + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/DependencyConditionMatchesConditionalDependencyTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/DependencyConditionMatchesConditionalDependencyTest.java new file mode 100644 index 00000000000000..0795f851ea6c03 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/DependencyConditionMatchesConditionalDependencyTest.java @@ -0,0 +1,50 @@ +package io.quarkus.deployment.conditionaldeps; + +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class DependencyConditionMatchesConditionalDependencyTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + extD.setDependencyCondition(extB); + extD.setConditionalDeps(extB); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extD); + + install(extA); + install(extB); + install(extC); + install(extD); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extB.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC) + .addDependency(extA); + } + + @Override + protected String[] expectedExtensionDependencies() { + return new String[] { + "ext-a", + "ext-b", + "ext-c", + "ext-d" + }; + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedDirectConditionalDependencyTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedDirectConditionalDependencyTest.java new file mode 100644 index 00000000000000..13ab687487d3e9 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedDirectConditionalDependencyTest.java @@ -0,0 +1,49 @@ +package io.quarkus.deployment.conditionaldeps; + +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class ExcludedDirectConditionalDependencyTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA); + install(extB); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + + final TsQuarkusExt extE = new TsQuarkusExt("ext-e"); + extE.setDependencyCondition(extD); + install(extE); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extB, extE); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + addToExpectedLib(extE.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC, extB.getRuntime()) + .addDependency(extA) + .addDependency(extD); + } + + @Override + protected String[] expectedExtensionDependencies() { + return new String[] { + "ext-a", + "ext-c", + "ext-d", + "ext-e" + }; + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedTransitiveConditionalDependencyTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedTransitiveConditionalDependencyTest.java new file mode 100644 index 00000000000000..3a225286fa6b44 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/ExcludedTransitiveConditionalDependencyTest.java @@ -0,0 +1,43 @@ +package io.quarkus.deployment.conditionaldeps; + +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class ExcludedTransitiveConditionalDependencyTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA); + install(extB); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extB); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + extD.addDependency(extC); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extD, extB.getRuntime()) + .addDependency(extA); + } + + @Override + protected String[] expectedExtensionDependencies() { + return new String[] { + "ext-a", + "ext-c", + "ext-d" + }; + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/TransitiveConditionalDepTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/TransitiveConditionalDepTest.java new file mode 100644 index 00000000000000..233f47a4b70aa0 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/TransitiveConditionalDepTest.java @@ -0,0 +1,71 @@ +package io.quarkus.deployment.conditionaldeps; + +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class TransitiveConditionalDepTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + // these H and I are not going to be satisfied + final TsQuarkusExt extH = new TsQuarkusExt("ext-h"); + install(extH); + final TsQuarkusExt extIConditional = new TsQuarkusExt("ext-i-conditional"); + extIConditional.setDependencyCondition(extH); + install(extIConditional); + + final TsQuarkusExt extGConditional = new TsQuarkusExt("ext-g-conditional"); + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + extA.setConditionalDeps(extGConditional); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.addDependency(extA); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.addDependency(extB); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + extD.addDependency(extB); + + final TsQuarkusExt extEConditional = new TsQuarkusExt("ext-e-conditional"); + extEConditional.setDependencyCondition(extB); + install(extEConditional); + + final TsQuarkusExt extF = new TsQuarkusExt("ext-f"); + extF.setConditionalDeps(extEConditional, extIConditional); + + extGConditional.setDependencyCondition(extC); + extGConditional.addDependency(extF); + install(extGConditional); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extB.getRuntime()); + addToExpectedLib(extC.getRuntime()); + addToExpectedLib(extD.getRuntime()); + addToExpectedLib(extEConditional.getRuntime()); + addToExpectedLib(extF.getRuntime()); + addToExpectedLib(extGConditional.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC) + .addDependency(extD); + } + + @Override + protected String[] expectedExtensionDependencies() { + return new String[] { + "ext-a", + "ext-b", + "ext-c", + "ext-d", + "ext-e-conditional", + "ext-f", + "ext-g-conditional" + }; + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/UnsatisfiedConditionalDependencyWithTwoConditionsTest.java b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/UnsatisfiedConditionalDependencyWithTwoConditionsTest.java new file mode 100644 index 00000000000000..264845f459be38 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/conditionaldeps/UnsatisfiedConditionalDependencyWithTwoConditionsTest.java @@ -0,0 +1,49 @@ +package io.quarkus.deployment.conditionaldeps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppDependency; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.deployment.runnerjar.ExecutableOutputOutcomeTestBase; + +public class UnsatisfiedConditionalDependencyWithTwoConditionsTest extends ExecutableOutputOutcomeTestBase { + + @Override + protected TsArtifact modelApp() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + extB.setDependencyCondition(extA, extD); + install(extB); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setConditionalDeps(extB); + + addToExpectedLib(extA.getRuntime()); + addToExpectedLib(extC.getRuntime()); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extC) + .addDependency(extA); + } + + @Override + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { + final Set expected = new HashSet<>(); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-c-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + expected.add(new AppDependency( + new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, "ext-a-deployment", TsArtifact.DEFAULT_VERSION), "compile")); + assertEquals(expected, new HashSet<>(deploymentDeps)); + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ExecutableOutputOutcomeTestBase.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ExecutableOutputOutcomeTestBase.java index c6cf3c650c7b39..00013e201311c4 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ExecutableOutputOutcomeTestBase.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ExecutableOutputOutcomeTestBase.java @@ -27,6 +27,8 @@ import io.quarkus.bootstrap.app.AugmentResult; import io.quarkus.bootstrap.app.CuratedApplication; import io.quarkus.bootstrap.app.QuarkusBootstrap; +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact.ContentProvider; @@ -46,9 +48,16 @@ protected void addToExpectedLib(TsArtifact entry) { expectedLib.add(entry.getGroupId() + '.' + entry.getArtifactId() + '-' + entry.getVersion() + '.' + entry.getType()); } + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { + } + protected void assertAppModel(AppModel appModel) throws Exception { } + protected String[] expectedExtensionDependencies() { + return null; + } + protected TsDependency platformDescriptor() { if (platformDescriptor == null) { TsArtifact platformDescr = new TsArtifact("org.acme", @@ -107,6 +116,11 @@ protected void testCreator(QuarkusBootstrap creator) throws Exception { try { CuratedApplication curated = creator.bootstrap(); assertAppModel(curated.getAppModel()); + final String[] expectedExtensions = expectedExtensionDependencies(); + if (expectedExtensions != null) { + assertExtensionDependencies(curated.getAppModel(), expectedExtensions); + } + assertDeploymentDeps(curated.getAppModel().getDeploymentDependencies()); AugmentAction action = curated.createAugmentor(); AugmentResult outcome = action.createProductionApplication(); @@ -178,4 +192,24 @@ protected void testCreator(QuarkusBootstrap creator) throws Exception { System.clearProperty("quarkus.package.type"); } } + + private static void assertExtensionDependencies(AppModel appModel, String[] expectedExtensions) { + final Set expectedRuntime = new HashSet<>(expectedExtensions.length); + final Set expectedDeployment = new HashSet<>(expectedExtensions.length); + for (String rtId : expectedExtensions) { + expectedRuntime.add(new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, rtId, TsArtifact.DEFAULT_VERSION)); + expectedDeployment + .add(new AppArtifact(TsArtifact.DEFAULT_GROUP_ID, rtId + "-deployment", TsArtifact.DEFAULT_VERSION)); + } + + for (AppDependency dep : appModel.getUserDependencies()) { + assertTrue(expectedRuntime.contains(dep.getArtifact()), dep.getArtifact().toString()); + } + assertEquals(expectedExtensions.length, appModel.getUserDependencies().size()); + + for (AppDependency dep : appModel.getDeploymentDependencies()) { + assertTrue(expectedDeployment.contains(dep.getArtifact())); + } + assertEquals(expectedExtensions.length, appModel.getDeploymentDependencies().size()); + } } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/OptionalDepsTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/OptionalDepsTest.java index 091a0118a0e17d..764e145dd906d5 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/OptionalDepsTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/OptionalDepsTest.java @@ -3,11 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashSet; +import java.util.List; import java.util.Set; import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppDependency; -import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsDependency; import io.quarkus.bootstrap.resolver.TsQuarkusExt; @@ -66,7 +66,7 @@ protected TsArtifact modelApp() { } @Override - protected void assertAppModel(AppModel appModel) throws Exception { + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { final Set expected = new HashSet<>(); expected.add(new AppDependency(new AppArtifact("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), "compile", true)); expected.add( @@ -74,6 +74,6 @@ protected void assertAppModel(AppModel appModel) throws Exception { expected.add(new AppDependency(new AppArtifact("io.quarkus.bootstrap.test", "ext-b-deployment", "1"), "compile", true)); expected.add( new AppDependency(new AppArtifact("io.quarkus.bootstrap.test", "ext-d-deployment", "1"), "compile", false)); - assertEquals(expected, new HashSet<>(appModel.getDeploymentDependencies())); + assertEquals(expected, new HashSet<>(deploymentDeps)); } } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java index fd05600da9303a..fa09f84adb0ded 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java @@ -3,11 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashSet; +import java.util.List; import java.util.Set; import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppDependency; -import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsDependency; import io.quarkus.bootstrap.resolver.TsQuarkusExt; @@ -41,10 +41,10 @@ protected TsArtifact modelApp() { } @Override - protected void assertAppModel(AppModel appModel) throws Exception { + protected void assertDeploymentDeps(List deploymentDeps) throws Exception { final Set expected = new HashSet<>(); expected.add(new AppDependency(new AppArtifact("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), "compile")); expected.add(new AppDependency(new AppArtifact("io.quarkus.bootstrap.test", "ext-a-deployment-dep", "1"), "compile")); - assertEquals(expected, new HashSet<>(appModel.getDeploymentDependencies())); + assertEquals(expected, new HashSet<>(deploymentDeps)); } } diff --git a/core/launcher/src/main/java/io/quarkus/launcher/JBangIntegration.java b/core/launcher/src/main/java/io/quarkus/launcher/JBangIntegration.java index 9d548efe5e74fe..3cd98e58c03f0d 100644 --- a/core/launcher/src/main/java/io/quarkus/launcher/JBangIntegration.java +++ b/core/launcher/src/main/java/io/quarkus/launcher/JBangIntegration.java @@ -32,7 +32,6 @@ public static Map postBuild(Path appClasses, Path pomFile, List< String pomContents = String.join("\n", Files.readAllLines(pomFile)); dataOutputStream.writeUTF(pomContents); //Source file locations - dataOutputStream.writeUTF(appClasses.toAbsolutePath().toString()); dataOutputStream.writeUTF(System.getProperty("jbang.source")); dataOutputStream.writeInt(originalDeps.size()); for (Map.Entry i : originalDeps) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java index 372b4a0adbc938..cc93df1a2c9cc0 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java @@ -94,7 +94,7 @@ public void run() { long start = System.nanoTime(); for (;;) try { - if (!executor.awaitTermination(Math.min(remaining, intervalRemaining), TimeUnit.MILLISECONDS)) { + if (!executor.awaitTermination(Math.min(remaining, intervalRemaining), TimeUnit.NANOSECONDS)) { long elapsed = System.nanoTime() - start; intervalRemaining -= elapsed; remaining -= elapsed; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/LiveReloadConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/LiveReloadConfig.java index 19b32bd5ece1d9..15b50771df6bb6 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/LiveReloadConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/LiveReloadConfig.java @@ -44,4 +44,16 @@ public class LiveReloadConfig { */ @ConfigItem(defaultValue = "30s") public Duration connectTimeout; + + /** + * The amount of time to wait between attempts when connecting to the server side of remote dev + */ + @ConfigItem(defaultValue = "2s") + public Duration retryInterval; + + /** + * The maximum number of attempts when connecting to the server side of remote dev + */ + @ConfigItem(defaultValue = "10") + public Integer retryMaxAttempts; } 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 c8628317f3ee79..55420f3e272be8 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 @@ -6,7 +6,6 @@ import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE_PARENT; import static io.smallrye.config.PropertiesConfigSourceProvider.classPathSources; import static io.smallrye.config.SmallRyeConfigBuilder.META_INF_MICROPROFILE_CONFIG_PROPERTIES; -import static io.smallrye.config.SmallRyeConfigBuilder.WEB_INF_MICROPROFILE_CONFIG_PROPERTIES; import java.io.IOException; import java.net.URL; @@ -101,7 +100,6 @@ public OptionalInt getPriority() { } else { final List sources = new ArrayList<>(); sources.addAll(classPathSources(META_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader)); - sources.addAll(classPathSources(WEB_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader)); sources.addAll(new BuildTimeDotEnvConfigSourceProvider().getConfigSources(classLoader)); sources.add(new BuildTimeEnvConfigSource()); sources.add(new BuildTimeSysPropConfigSource()); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/graal/LoggingSubstitutions.java b/core/runtime/src/main/java/io/quarkus/runtime/graal/LoggingSubstitutions.java index 2b1967c2b7a690..560493a4074fd9 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/graal/LoggingSubstitutions.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/graal/LoggingSubstitutions.java @@ -1,5 +1,6 @@ package io.quarkus.runtime.graal; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Handler; import org.jboss.logmanager.LogContext; @@ -20,8 +21,8 @@ final class Target_org_jboss_logmanager_LoggerNode { @Alias - @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) - volatile Handler[] handlers; + @RecomputeFieldValue(declClass = AtomicReference.class, kind = RecomputeFieldValue.Kind.NewInstance) + AtomicReference handlersRef; } @TargetClass(className = "org.slf4j.LoggerFactory") diff --git a/core/runtime/src/main/java/io/quarkus/runtime/graal/MappedByteBufferReplacement.java b/core/runtime/src/main/java/io/quarkus/runtime/graal/MappedByteBufferReplacement.java index f98382534aabb1..4b8967faffba18 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/graal/MappedByteBufferReplacement.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/graal/MappedByteBufferReplacement.java @@ -5,8 +5,9 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.jdk.JDK15OrEarlier; -@TargetClass(MappedByteBuffer.class) +@TargetClass(value = MappedByteBuffer.class, onlyWith = JDK15OrEarlier.class) final class MappedByteBufferReplacement { @Substitute diff --git a/core/runtime/src/main/java/io/quarkus/runtime/util/JavaVersionUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/util/JavaVersionUtil.java index 74d7d0c31ea06f..082a6378b0ebf3 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/util/JavaVersionUtil.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/util/JavaVersionUtil.java @@ -9,6 +9,7 @@ public class JavaVersionUtil { private static boolean IS_JAVA_11_OR_NEWER; private static boolean IS_JAVA_13_OR_NEWER; + private static boolean IS_GRAALVM_JDK; static { performChecks(); @@ -25,6 +26,9 @@ static void performChecks() { IS_JAVA_11_OR_NEWER = false; IS_JAVA_13_OR_NEWER = false; } + + String vmVendor = System.getProperty("java.vm.vendor"); + IS_GRAALVM_JDK = (vmVendor != null) && vmVendor.startsWith("GraalVM"); } public static boolean isJava11OrHigher() { @@ -34,4 +38,8 @@ public static boolean isJava11OrHigher() { public static boolean isJava13OrHigher() { return IS_JAVA_13_OR_NEWER; } + + public static boolean isGraalvmJdk() { + return IS_GRAALVM_JDK; + } } diff --git a/devtools/bom-descriptor-json/pom.xml b/devtools/bom-descriptor-json/pom.xml index 348d3c8d8f245e..8daf106675e5d1 100644 --- a/devtools/bom-descriptor-json/pom.xml +++ b/devtools/bom-descriptor-json/pom.xml @@ -48,13 +48,12 @@ ${pluginPhase} - generate-platform-descriptor-json - - software.amazon.awssdk - + + io.quarkus + quarkus-bom true ${basedir}/target/resources/catalog-overrides.json @@ -294,6 +293,19 @@ + + io.quarkus + quarkus-apache-httpclient + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-arc @@ -1217,6 +1229,19 @@ + + io.quarkus + quarkus-kubernetes-client-internal + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-kubernetes-config @@ -1581,6 +1606,32 @@ + + io.quarkus + quarkus-opentelemetry + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-picocli @@ -1776,6 +1827,19 @@ + + io.quarkus + quarkus-rest-client-reactive-jackson + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy @@ -1932,6 +1996,19 @@ + + io.quarkus + quarkus-resteasy-reactive-jackson-common + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy-reactive-jsonb @@ -1945,6 +2022,19 @@ + + io.quarkus + quarkus-resteasy-reactive-links + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy-reactive-qute @@ -2496,4 +2586,4 @@ - \ No newline at end of file + diff --git a/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json b/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json index 70058854286731..b0d69485bacb68 100644 --- a/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json +++ b/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json @@ -138,7 +138,7 @@ } }, "codestarts-artifacts": [ - "${project.groupId}:quarkus-platform-descriptor-json::jar:${project.version}" + "${project.groupId}:quarkus-project-core-extension-codestarts::jar:${project.version}" ] } } \ No newline at end of file diff --git a/devtools/cli/pom.xml b/devtools/cli/pom.xml index 582a33a4e773b6..d72ceffa76c7a1 100644 --- a/devtools/cli/pom.xml +++ b/devtools/cli/pom.xml @@ -24,7 +24,7 @@ io.quarkus - quarkus-platform-descriptor-json + quarkus-project-core-extension-codestarts io.quarkus diff --git a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java index 83338f67734029..045ebc391078ae 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java @@ -18,11 +18,10 @@ import picocli.CommandLine; @QuarkusMain -@CommandLine.Command(name = "quarkus", aliases = { - "qs" }, versionProvider = QuarkusCliVersion.class, usageHelpAutoWidth = true, subcommandsRepeatable = true, mixinStandardHelpOptions = true, subcommands = { - Build.class, - Clean.class, Create.class, CreateJBang.class, List.class, Platforms.class, Add.class, Remove.class, Dev.class, - CreateExtension.class }) +@CommandLine.Command(name = "qs", versionProvider = QuarkusCliVersion.class, usageHelpAutoWidth = true, subcommandsRepeatable = true, mixinStandardHelpOptions = true, subcommands = { + Build.class, + Clean.class, Create.class, CreateJBang.class, List.class, Platforms.class, Add.class, Remove.class, Dev.class, + CreateExtension.class }) public class QuarkusCli implements QuarkusApplication { public void usage() { diff --git a/devtools/cli/src/main/java/io/quarkus/cli/core/ExecuteUtil.java b/devtools/cli/src/main/java/io/quarkus/cli/core/ExecuteUtil.java index eb1f618b9b4136..c9aaa6035afdda 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/core/ExecuteUtil.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/core/ExecuteUtil.java @@ -9,10 +9,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -78,11 +75,10 @@ private static int executeMaven(File projectDirectory, QuarkusCli cli, String... } private static String findExecutable(String exec) { - Optional mvnPath = Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))) - .map(Paths::get) - .filter(path -> Files.exists(path.resolve(exec))).findFirst(); - - return mvnPath.map(value -> value.getParent().toString()).orElse(null); + return Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))) + .map(Paths::get).map(path -> path.resolve(exec).toFile()) + .filter(File::exists).findFirst() + .map(File::getParent).orElse(null); } private static int executeWrapper(File wrapper, QuarkusCli cli, String... args) throws Exception { diff --git a/devtools/gradle/build.gradle b/devtools/gradle/build.gradle index e55bfd1ad8d18f..086e7756c81122 100644 --- a/devtools/gradle/build.gradle +++ b/devtools/gradle/build.gradle @@ -32,9 +32,9 @@ dependencies { implementation "io.quarkus:quarkus-bootstrap-core:${version}" implementation "io.quarkus:quarkus-devtools-common:${version}" - implementation "io.quarkus:quarkus-platform-descriptor-json:${version}" implementation "io.quarkus:quarkus-core-deployment:${version}" + testImplementation "io.quarkus:quarkus-project-core-extension-codestarts:${version}" testImplementation 'org.mockito:mockito-core:3.8.0' testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' diff --git a/devtools/gradle/pom.xml b/devtools/gradle/pom.xml index cfc5f3bf44de11..d4de2d4396980c 100644 --- a/devtools/gradle/pom.xml +++ b/devtools/gradle/pom.xml @@ -43,7 +43,15 @@ io.quarkus - quarkus-platform-descriptor-json + quarkus-project-core-extension-codestarts + pom + test + + + * + * + + org.awaitility diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java index 8a1115b1a96f55..5bc7725896f36c 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java @@ -409,10 +409,16 @@ private void addDevModePaths(final DependencyImpl dep, ResolvedArtifact a, Proje dep.addPath(a.getFile()); return; } - - final File classesDir = new File(QuarkusGradleUtils.getClassesDir(mainSourceSet, project.getBuildDir(), false)); - if (classesDir.exists()) { - dep.addPath(classesDir); + final String classes = QuarkusGradleUtils.getClassesDir(mainSourceSet, project.getBuildDir(), false); + if (classes == null) { + dep.addPath(a.getFile()); + } else { + final File classesDir = new File(classes); + if (classesDir.exists()) { + dep.addPath(classesDir); + } else { + dep.addPath(a.getFile()); + } } for (File resourcesDir : mainSourceSet.getResources().getSourceDirectories()) { if (resourcesDir.exists()) { @@ -458,13 +464,18 @@ private IncludedBuild includedBuild(final Project project, final String projectN } private SourceSetImpl convert(SourceSet sourceSet) { + Set existingSrcDirs = new HashSet<>(); + for (File srcDir : sourceSet.getOutput().getClassesDirs().getFiles()) { + if (srcDir.exists()) { + existingSrcDirs.add(srcDir); + } + } if (sourceSet.getOutput().getResourcesDir().exists()) { return new SourceSetImpl( - sourceSet.getOutput().getClassesDirs().getFiles(), + existingSrcDirs, sourceSet.getOutput().getResourcesDir()); } - return new SourceSetImpl( - sourceSet.getOutput().getClassesDirs().getFiles()); + return new SourceSetImpl(existingSrcDirs); } private io.quarkus.bootstrap.resolver.model.SourceSet getSourceSourceSet(SourceSet sourceSet) { 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 d8c94a0a74494b..2c0a90d0415db7 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 @@ -236,11 +236,21 @@ private QuarkusDevModeLauncher newLauncher() throws Exception { for (AppDependency appDependency : appModel.getFullDeploymentDeps()) { final AppArtifact appArtifact = appDependency.getArtifact(); + //we only use the launcher for launching from the IDE, we need to exclude it + if (appArtifact.getGroupId().equals("io.quarkus") && appArtifact.getGroupId().equals("quarkus-ide-launcher")) { + continue; + } if (!projectDependencies.contains(new AppArtifactKey(appArtifact.getGroupId(), appArtifact.getArtifactId()))) { appArtifact.getPaths().forEach(p -> { if (Files.exists(p)) { - addToClassPaths(builder, p.toFile()); + if (appArtifact.getGroupId().equals("io.quarkus") + && appArtifact.getArtifactId().equals("quarkus-class-change-agent")) { + builder.jvmArgs("-javaagent:" + p.toFile().getAbsolutePath()); + } else { + addToClassPaths(builder, p.toFile()); + } } + }); } } @@ -335,6 +345,14 @@ private void addLocalProject(Project project, GradleDevModeLauncher.Builder buil } String classesDir = QuarkusGradleUtils.getClassesDir(mainSourceSet, project.getBuildDir()); + if (classesDir == null) { + return; + } else { + File classesDirFile = new File(classesDir); + if (!classesDirFile.exists()) { + return; + } + } final String resourcesOutputPath; if (resourcesOutputDir.exists()) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGradleUtils.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGradleUtils.java index 47cae1fce874d2..9b5d936a13aef7 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGradleUtils.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGradleUtils.java @@ -101,7 +101,7 @@ public static String getClassesDir(SourceSet sourceSet, File tmpDir, boolean pop } } if (classesDir == null) { - throw new IllegalStateException("Failed to locate classes directory in the project"); + return null; } return classesDir.toString(); } diff --git a/devtools/maven/pom.xml b/devtools/maven/pom.xml index cc5d536ed0996f..7f63fd78be582e 100644 --- a/devtools/maven/pom.xml +++ b/devtools/maven/pom.xml @@ -34,7 +34,7 @@ io.quarkus - quarkus-platform-descriptor-json + quarkus-project-core-extension-codestarts io.quarkus @@ -145,6 +145,12 @@ + + io.quarkus + quarkus-bootstrap-core + test-jar + test + org.assertj assertj-core diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DependencyTreeMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DependencyTreeMojo.java new file mode 100644 index 00000000000000..8f72689733ea71 --- /dev/null +++ b/devtools/maven/src/main/java/io/quarkus/maven/DependencyTreeMojo.java @@ -0,0 +1,87 @@ +package io.quarkus.maven; + +import java.util.List; + +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.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.aether.repository.RemoteRepository; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; +import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; +import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; + +/** + * Displays Quarkus application build dependency tree including the deployment ones. + */ +@Mojo(name = "dependency-tree", defaultPhase = LifecyclePhase.NONE, requiresDependencyResolution = ResolutionScope.NONE) +public class DependencyTreeMojo extends AbstractMojo { + + @Component + protected QuarkusBootstrapProvider bootstrapProvider; + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + protected MavenProject project; + + @Parameter(defaultValue = "${project.remoteRepositories}", readonly = true, required = true) + private List repos; + + /** + * Target launch mode corresponding to {@link io.quarkus.runtime.LaunchMode} for which the dependency tree should be built. + * {@link io.quarkus.runtime.LaunchMode.PROD} is the default. + */ + @Parameter(property = "mode", required = false, defaultValue = "prod") + String mode; + + protected MavenArtifactResolver resolver; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + final StringBuilder buf = new StringBuilder(); + buf.append("Quarkus application ").append(mode.toUpperCase()).append(" mode build dependency tree:"); + getLog().info(buf.toString()); + + final AppArtifact appArtifact = new AppArtifact(project.getGroupId(), project.getArtifactId(), null, "pom", + project.getVersion()); + final BootstrapAppModelResolver modelResolver; + try { + modelResolver = new BootstrapAppModelResolver(resolver()); + if (mode != null) { + if (mode.equalsIgnoreCase("test")) { + modelResolver.setTest(true); + } else if (mode.equalsIgnoreCase("dev") || mode.equalsIgnoreCase("development")) { + modelResolver.setDevMode(true); + } else if (mode.equalsIgnoreCase("prod") || mode.isEmpty()) { + // ignore, that's the default + } else { + throw new MojoExecutionException( + "Parameter 'mode' was set to '" + mode + "' while expected one of 'dev', 'test' or 'prod'"); + } + } + modelResolver.setBuildTreeLogger(s -> getLog().info(s)); + modelResolver.resolveModel(appArtifact); + } catch (Exception e) { + throw new MojoExecutionException("Failed to resolve application model " + appArtifact + " dependencies", e); + } + } + + protected MavenArtifactResolver resolver() throws BootstrapMavenException { + return resolver == null + ? resolver = MavenArtifactResolver.builder() + .setRepositorySystem(bootstrapProvider.repositorySystem()) + .setRemoteRepositoryManager(bootstrapProvider.remoteRepositoryManager()) + //.setRepositorySystemSession(repoSession) the session should be initialized with the loaded workspace + .setRemoteRepositories(repos) + .build() + : resolver; + } + +} diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 0978339a704777..38bad7177a3821 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -229,6 +229,9 @@ public class DevMojo extends AbstractMojo { @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true) private List repos; + @Parameter(defaultValue = "${project.remotePluginRepositories}", readonly = true, required = true) + private List pluginRepos; + /** * This value is intended to be set to true when some generated bytecode * is erroneous causing the JVM to crash when the verify:none option is set (which is on by default) @@ -472,7 +475,7 @@ private Xpp3Dom getPluginConfig(Plugin plugin, String goal) throws MojoExecution private MojoDescriptor getMojoDescriptor(Plugin plugin, String goal) throws MojoExecutionException { try { - return pluginManager.getMojoDescriptor(plugin, goal, repos, repoSession); + return pluginManager.getMojoDescriptor(plugin, goal, pluginRepos, repoSession); } catch (Exception e) { throw new MojoExecutionException( "Failed to obtain descriptor for Maven plugin " + plugin.getId() + " goal " + goal, e); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapMojo.java index 1af17c8a296234..a0901817622288 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapMojo.java @@ -72,6 +72,30 @@ public abstract class QuarkusBootstrapMojo extends AbstractMojo { @Parameter(property = "ignoredEntries") private String[] ignoredEntries; + /** + * Coordinates of the Maven artifact containing the original Java application to build the native image for. + * If not provided, the current project is assumed to be the original Java application. + *

+ * The coordinates are expected to be expressed in the following format: + *

+ * groupId:artifactId:classifier:type:version + *

+ * With the classifier, type and version being optional. + *

+ * If the type is missing, the artifact is assumed to be of type JAR. + *

+ * If the version is missing, the artifact is going to be looked up among the project dependencies using the provided + * coordinates. + * + *

+ * However, if the expression consists of only three parts, it is assumed to be groupId:artifactId:version. + * + *

+ * If the expression consists of only four parts, it is assumed to be groupId:artifactId:classifier:type. + */ + @Parameter(required = false, property = "appArtifact") + private String appArtifact; + private AppArtifactKey projectId; @Override @@ -105,6 +129,10 @@ public void setLog(Log log) { */ protected abstract void doExecute() throws MojoExecutionException, MojoFailureException; + protected String appArtifactCoords() { + return appArtifact; + } + protected RepositorySystem repositorySystem() { return bootstrapProvider.repositorySystem(); } @@ -145,6 +173,8 @@ protected AppArtifactKey projectId() { return projectId == null ? projectId = new AppArtifactKey(project.getGroupId(), project.getArtifactId()) : projectId; } + // @deprecated in 1.14.0.Final + @Deprecated protected AppArtifact projectArtifact() throws MojoExecutionException { return bootstrapProvider.projectArtifact(this); } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java index c827cd3f450b31..e03e3efa055b63 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java @@ -12,6 +12,7 @@ import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.impl.RemoteRepositoryManager; import com.google.common.cache.Cache; @@ -60,9 +61,11 @@ public MavenArtifactResolver artifactResolver(QuarkusBootstrapMojo mojo) return provider(mojo.projectId()).artifactResolver(mojo); } + // @deprecated in 1.14.0.Final + @Deprecated public AppArtifact projectArtifact(QuarkusBootstrapMojo mojo) throws MojoExecutionException { - return provider(mojo.projectId()).projectArtifact(mojo); + return provider(mojo.projectId()).appArtifact(mojo); } public QuarkusBootstrap bootstrapQuarkus(QuarkusBootstrapMojo mojo) @@ -91,7 +94,7 @@ public void close() throws IOException { private class QuarkusAppBootstrapProvider implements Closeable { - private AppArtifact projectArtifact; + private AppArtifact appArtifact; private MavenArtifactResolver artifactResolver; private QuarkusBootstrap quarkusBootstrap; private CuratedApplication curatedApp; @@ -114,34 +117,6 @@ private MavenArtifactResolver artifactResolver(QuarkusBootstrapMojo mojo) } } - private AppArtifact projectArtifact(QuarkusBootstrapMojo mojo) throws MojoExecutionException { - if (projectArtifact != null) { - return projectArtifact; - } - final Artifact projectArtifact = mojo.mavenProject().getArtifact(); - final AppArtifact appArtifact = new AppArtifact(projectArtifact.getGroupId(), projectArtifact.getArtifactId(), - projectArtifact.getClassifier(), projectArtifact.getArtifactHandler().getExtension(), - projectArtifact.getVersion()); - - File projectFile = projectArtifact.getFile(); - if (projectFile == null) { - projectFile = new File(mojo.mavenProject().getBuild().getOutputDirectory()); - if (!projectFile.exists()) { - /* - * TODO GenerateCodeMojo would fail - * if (hasSources(project)) { - * throw new MojoExecutionException("Project " + project.getArtifact() + " has not been compiled yet"); - * } - */ - if (!projectFile.mkdirs()) { - throw new MojoExecutionException("Failed to create the output dir " + projectFile); - } - } - } - appArtifact.setPaths(PathsCollection.of(projectFile.toPath())); - return appArtifact; - } - protected QuarkusBootstrap bootstrapQuarkus(QuarkusBootstrapMojo mojo) throws MojoExecutionException { if (quarkusBootstrap != null) { return quarkusBootstrap; @@ -164,7 +139,8 @@ protected QuarkusBootstrap bootstrapQuarkus(QuarkusBootstrapMojo mojo) throws Mo effectiveProperties.putIfAbsent("quarkus.application.version", mojo.mavenProject().getVersion()); QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder() - .setAppArtifact(projectArtifact(mojo)) + .setAppArtifact(appArtifact(mojo)) + .setManagingProject(managingProject(mojo)) .setMavenArtifactResolver(artifactResolver(mojo)) .setIsolateDeployment(true) .setBaseClassLoader(getClass().getClassLoader()) @@ -195,6 +171,96 @@ protected CuratedApplication curateApplication(QuarkusBootstrapMojo mojo) throws } } + protected AppArtifact managingProject(QuarkusBootstrapMojo mojo) { + if (mojo.appArtifactCoords() == null) { + return null; + } + final Artifact artifact = mojo.mavenProject().getArtifact(); + return new AppArtifact(artifact.getGroupId(), artifact.getArtifactId(), + artifact.getClassifier(), artifact.getArtifactHandler().getExtension(), + artifact.getVersion()); + } + + private AppArtifact appArtifact(QuarkusBootstrapMojo mojo) throws MojoExecutionException { + return appArtifact == null ? appArtifact = initAppArtifact(mojo) : appArtifact; + } + + private AppArtifact initAppArtifact(QuarkusBootstrapMojo mojo) throws MojoExecutionException { + String appArtifactCoords = mojo.appArtifactCoords(); + if (appArtifactCoords == null) { + final Artifact projectArtifact = mojo.mavenProject().getArtifact(); + final AppArtifact appArtifact = new AppArtifact(projectArtifact.getGroupId(), projectArtifact.getArtifactId(), + projectArtifact.getClassifier(), projectArtifact.getArtifactHandler().getExtension(), + projectArtifact.getVersion()); + + File projectFile = projectArtifact.getFile(); + if (projectFile == null) { + projectFile = new File(mojo.mavenProject().getBuild().getOutputDirectory()); + if (!projectFile.exists()) { + /* + * TODO GenerateCodeMojo would fail + * if (hasSources(project)) { + * throw new MojoExecutionException("Project " + project.getArtifact() + " has not been compiled yet"); + * } + */ + if (!projectFile.mkdirs()) { + throw new MojoExecutionException("Failed to create the output dir " + projectFile); + } + } + } + appArtifact.setPaths(PathsCollection.of(projectFile.toPath())); + return appArtifact; + } + + final String[] coordsArr = appArtifactCoords.split(":"); + if (coordsArr.length < 2 || coordsArr.length > 5) { + throw new MojoExecutionException( + "appArtifact expression " + appArtifactCoords + + " does not follow format groupId:artifactId:classifier:type:version"); + } + final String groupId = coordsArr[0]; + final String artifactId = coordsArr[1]; + String classifier = ""; + String type = "jar"; + String version = null; + if (coordsArr.length == 3) { + version = coordsArr[2]; + } else if (coordsArr.length > 3) { + classifier = coordsArr[2] == null ? "" : coordsArr[2]; + type = coordsArr[3] == null ? "jar" : coordsArr[3]; + if (coordsArr.length > 4) { + version = coordsArr[4]; + } + } + if (version == null) { + for (Artifact dep : mojo.mavenProject().getArtifacts()) { + if (dep.getArtifactId().equals(artifactId) + && dep.getGroupId().equals(groupId) + && dep.getClassifier().equals(classifier) + && dep.getType().equals(type)) { + return new AppArtifact(dep.getGroupId(), + dep.getArtifactId(), + dep.getClassifier(), + dep.getArtifactHandler().getExtension(), + dep.getVersion()); + } + } + throw new IllegalStateException("Failed to locate " + appArtifactCoords + " among the project dependencies"); + } + + final AppArtifact appArtifact = new AppArtifact(groupId, artifactId, classifier, type, version); + try { + appArtifact.setPath( + artifactResolver(mojo).resolve(new DefaultArtifact(groupId, artifactId, classifier, type, version)) + .getArtifact().getFile().toPath()); + } catch (MojoExecutionException e) { + throw e; + } catch (Exception e) { + throw new MojoExecutionException("Failed to resolve " + appArtifact, e); + } + return appArtifact; + } + @Override public void close() throws IOException { if (curatedApp != null) { diff --git a/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java b/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java new file mode 100644 index 00000000000000..c89f2e5f57aaa5 --- /dev/null +++ b/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java @@ -0,0 +1,117 @@ +package io.quarkus.maven; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.model.Model; +import org.apache.maven.project.MavenProject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsDependency; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.bootstrap.resolver.TsRepoBuilder; +import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; +import io.quarkus.bootstrap.util.IoUtils; + +public abstract class DependencyTreeMojoTestBase { + protected Path workDir; + protected Path repoHome; + + protected MavenArtifactResolver mvnResolver; + protected TsRepoBuilder repoBuilder; + protected TsArtifact app; + protected Model appModel; + + @BeforeEach + public void setup() throws Exception { + workDir = IoUtils.createRandomTmpDir(); + repoHome = IoUtils.mkdirs(workDir.resolve("repo")); + + mvnResolver = MavenArtifactResolver.builder() + .setOffline(true) + .setLocalRepository(repoHome.toString()) + .setRemoteRepositories(Collections.emptyList()) + .build(); + + repoBuilder = TsRepoBuilder.getInstance(new BootstrapAppModelResolver(mvnResolver), workDir); + initRepo(); + } + + protected void initRepo() throws Exception { + final TsQuarkusExt coreExt = new TsQuarkusExt("test-core-ext"); + app = TsArtifact.jar("test-app") + .addDependency(new TsArtifact(TsArtifact.DEFAULT_GROUP_ID, "artifact-with-classifier", "classifier", "jar", + TsArtifact.DEFAULT_VERSION)) + .addDependency(new TsQuarkusExt("test-ext2") + .addDependency(new TsQuarkusExt("test-ext1").addDependency(coreExt))) + .addDependency(new TsDependency(TsArtifact.jar("optional"), true)) + .addDependency(new TsQuarkusExt("test-ext3").addDependency(coreExt)) + .addDependency(new TsDependency(TsArtifact.jar("provided"), "provided")) + .addDependency(new TsDependency(TsArtifact.jar("runtime"), "runtime")) + .addDependency(new TsDependency(TsArtifact.jar("test"), "test")); + appModel = app.getPomModel(); + app.install(repoBuilder); + } + + @AfterEach + public void cleanup() { + if (workDir != null) { + IoUtils.recursiveDelete(workDir); + } + } + + protected abstract String mode(); + + @Test + public void test() throws Exception { + + final DependencyTreeMojo mojo = new DependencyTreeMojo(); + mojo.project = new MavenProject(); + mojo.project.setArtifact(new DefaultArtifact(app.getGroupId(), app.getArtifactId(), app.getVersion(), "compile", + app.getType(), app.getClassifier(), new DefaultArtifactHandler("jar"))); + mojo.project.setModel(appModel); + mojo.project.setOriginalModel(appModel); + mojo.resolver = mvnResolver; + mojo.mode = mode(); + + final Path mojoLog = workDir.resolve("mojo.log"); + final PrintStream defaultOut = System.out; + + try (PrintStream logOut = new PrintStream(mojoLog.toFile(), "UTF-8")) { + System.setOut(logOut); + mojo.execute(); + } finally { + System.setOut(defaultOut); + } + + assertEquals(readInLowCase(Paths.get("").toAbsolutePath().resolve("target").resolve("test-classes") + .resolve(app.getArtifactFileName() + "." + mode())), readInLowCase(mojoLog)); + } + + private static List readInLowCase(Path p) throws IOException { + final List list = new ArrayList<>(); + try (BufferedReader reader = Files.newBufferedReader(p)) { + String line = reader.readLine(); + while (line != null) { + list.add(line.toLowerCase()); + line = reader.readLine(); + } + } + return list; + } +} diff --git a/devtools/maven/src/test/java/io/quarkus/maven/DevDependencyTreeMojoTest.java b/devtools/maven/src/test/java/io/quarkus/maven/DevDependencyTreeMojoTest.java new file mode 100644 index 00000000000000..bef26188da25e3 --- /dev/null +++ b/devtools/maven/src/test/java/io/quarkus/maven/DevDependencyTreeMojoTest.java @@ -0,0 +1,8 @@ +package io.quarkus.maven; + +public class DevDependencyTreeMojoTest extends DependencyTreeMojoTestBase { + @Override + protected String mode() { + return "dev"; + } +} diff --git a/devtools/maven/src/test/java/io/quarkus/maven/ProdDependencyTreeMojoTest.java b/devtools/maven/src/test/java/io/quarkus/maven/ProdDependencyTreeMojoTest.java new file mode 100644 index 00000000000000..81aa3c190b2586 --- /dev/null +++ b/devtools/maven/src/test/java/io/quarkus/maven/ProdDependencyTreeMojoTest.java @@ -0,0 +1,8 @@ +package io.quarkus.maven; + +public class ProdDependencyTreeMojoTest extends DependencyTreeMojoTestBase { + @Override + protected String mode() { + return "prod"; + } +} diff --git a/devtools/maven/src/test/java/io/quarkus/maven/TestDependencyTreeMojoTest.java b/devtools/maven/src/test/java/io/quarkus/maven/TestDependencyTreeMojoTest.java new file mode 100644 index 00000000000000..fab83b72936e58 --- /dev/null +++ b/devtools/maven/src/test/java/io/quarkus/maven/TestDependencyTreeMojoTest.java @@ -0,0 +1,8 @@ +package io.quarkus.maven; + +public class TestDependencyTreeMojoTest extends DependencyTreeMojoTestBase { + @Override + protected String mode() { + return "test"; + } +} diff --git a/devtools/maven/src/test/resources/test-app-1.jar.dev b/devtools/maven/src/test/resources/test-app-1.jar.dev new file mode 100644 index 00000000000000..1fb959fdae99b4 --- /dev/null +++ b/devtools/maven/src/test/resources/test-app-1.jar.dev @@ -0,0 +1,14 @@ +[info] quarkus application dev mode build dependency tree: +[info] io.quarkus.bootstrap.test:test-app:pom:1 +[info] ├─ io.quarkus.bootstrap.test:artifact-with-classifier:jar:classifier:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:test-ext2-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext2:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-ext1:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-ext1-deployment:jar:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:optional:jar:1 (compile optional) +[info] ├─ io.quarkus.bootstrap.test:test-ext3-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext3:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-core-ext:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-core-ext-deployment:jar:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:provided:jar:1 (provided) +[info] └─ io.quarkus.bootstrap.test:runtime:jar:1 (runtime) diff --git a/devtools/maven/src/test/resources/test-app-1.jar.prod b/devtools/maven/src/test/resources/test-app-1.jar.prod new file mode 100644 index 00000000000000..5c460135a1273a --- /dev/null +++ b/devtools/maven/src/test/resources/test-app-1.jar.prod @@ -0,0 +1,13 @@ +[info] quarkus application prod mode build dependency tree: +[info] io.quarkus.bootstrap.test:test-app:pom:1 +[info] ├─ io.quarkus.bootstrap.test:artifact-with-classifier:jar:classifier:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:test-ext2-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext2:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-ext1:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-ext1-deployment:jar:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:optional:jar:1 (compile optional) +[info] ├─ io.quarkus.bootstrap.test:test-ext3-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext3:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-core-ext:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-core-ext-deployment:jar:1 (compile) +[info] └─ io.quarkus.bootstrap.test:runtime:jar:1 (runtime) diff --git a/devtools/maven/src/test/resources/test-app-1.jar.test b/devtools/maven/src/test/resources/test-app-1.jar.test new file mode 100644 index 00000000000000..3f2160b370767f --- /dev/null +++ b/devtools/maven/src/test/resources/test-app-1.jar.test @@ -0,0 +1,14 @@ +[info] quarkus application test mode build dependency tree: +[info] io.quarkus.bootstrap.test:test-app:pom:1 +[info] ├─ io.quarkus.bootstrap.test:artifact-with-classifier:jar:classifier:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:test-ext2-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext2:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-ext1:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-ext1-deployment:jar:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:optional:jar:1 (compile optional) +[info] ├─ io.quarkus.bootstrap.test:test-ext3-deployment:jar:1 (compile) +[info] │ ├─ io.quarkus.bootstrap.test:test-ext3:jar:1 (compile) +[info] │ │ └─ io.quarkus.bootstrap.test:test-core-ext:jar:1 (compile) +[info] │ └─ io.quarkus.bootstrap.test:test-core-ext-deployment:jar:1 (compile) +[info] ├─ io.quarkus.bootstrap.test:runtime:jar:1 (runtime) +[info] └─ io.quarkus.bootstrap.test:test:jar:1 (test) diff --git a/devtools/platform-descriptor-json-plugin/src/main/java/io/quarkus/maven/GeneratePlatformDescriptorJsonMojo.java b/devtools/platform-descriptor-json-plugin/src/main/java/io/quarkus/maven/GeneratePlatformDescriptorJsonMojo.java index b90c02ece37fbd..1595f84d8fecce 100644 --- a/devtools/platform-descriptor-json-plugin/src/main/java/io/quarkus/maven/GeneratePlatformDescriptorJsonMojo.java +++ b/devtools/platform-descriptor-json-plugin/src/main/java/io/quarkus/maven/GeneratePlatformDescriptorJsonMojo.java @@ -108,12 +108,21 @@ public class GeneratePlatformDescriptorJsonMojo extends AbstractMojo { private MavenProjectHelper projectHelper; /** - * Group ID's that we know don't contain extensions. This can speed up the process - * by preventing the download of artifacts that are not required. + * A set of artifact group ID's that should be excluded from of the BOM and the descriptor. + * This can speed up the process by preventing the download of artifacts that are not required. */ @Parameter private Set ignoredGroupIds = new HashSet<>(0); + /** + * A set of group IDs artifacts of which should be checked to be extensions and if so, included into the + * generated descriptor. If this parameter is configured, artifacts with group IDs that aren't found + * among the configured set will be ignored. However, this will not prevent extensions that are inherited + * from parent platforms with different group IDs to be included into the generated descriptor. + */ + @Parameter + private Set processGroupIds = new HashSet<>(1); + /** * Skips the check for the descriptor's artifactId naming convention */ @@ -246,6 +255,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { for (Dependency dep : deps) { final Artifact artifact = dep.getArtifact(); + // checking whether the descriptor is present in the BOM if (!skipBomCheck && !jsonFoundInBom) { jsonFoundInBom = artifact.getArtifactId().equals(jsonArtifact.getArtifactId()) && artifact.getGroupId().equals(jsonArtifact.getGroupId()) @@ -254,11 +264,20 @@ public void execute() throws MojoExecutionException, MojoFailureException { && artifact.getVersion().equals(jsonArtifact.getVersion()); } - if (ignoredGroupIds.contains(artifact.getGroupId()) - || !artifact.getExtension().equals("jar") + // filtering non jar artifacts + if (!artifact.getExtension().equals("jar") || "javadoc".equals(artifact.getClassifier()) || "tests".equals(artifact.getClassifier()) - || "sources".equals(artifact.getClassifier())) { + || "sources".equals(artifact.getClassifier()) + || artifact.getArtifactId().endsWith("-deployment")) { + continue; + } + + if (processGroupIds.isEmpty()) { + if (ignoredGroupIds.contains(artifact.getGroupId())) { + continue; + } + } else if (!processGroupIds.contains(artifact.getGroupId())) { continue; } @@ -487,10 +506,6 @@ private List readDependencyManagement(Path pomXml) throws MojoExecut } private JsonExtension processDependency(Artifact artifact) throws IOException { - return processDependencyToObjectNode(artifact); - } - - private JsonExtension processDependencyToObjectNode(Artifact artifact) throws IOException { final Path path = artifact.getFile().toPath(); if (Files.isDirectory(path)) { return processMetaInfDir(artifact, path.resolve(BootstrapConstants.META_INF)); @@ -517,30 +532,28 @@ private JsonExtension processMetaInfDir(Artifact artifact, Path metaInfDir) if (!Files.exists(metaInfDir)) { return null; } - Path jsonOrYaml = null; Path yaml = metaInfDir.resolve(BootstrapConstants.QUARKUS_EXTENSION_FILE_NAME); if (Files.exists(yaml)) { mapper = getMapper(true); - jsonOrYaml = yaml; - } else { - mapper = getMapper(false); - Path json = metaInfDir.resolve(BootstrapConstants.EXTENSION_PROPS_JSON_FILE_NAME); - if (!Files.exists(json)) { - final Path props = metaInfDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME); - if (Files.exists(props)) { - final JsonExtension e = new JsonExtension(); - e.setArtifact(new ArtifactCoords(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), - artifact.getExtension(), artifact.getVersion())); - e.setName(artifact.getArtifactId()); - return e; - } - return null; - } else { - jsonOrYaml = json; + return processPlatformArtifact(artifact, yaml, mapper); + } + + JsonExtension e = null; + mapper = getMapper(false); + Path json = metaInfDir.resolve(BootstrapConstants.EXTENSION_PROPS_JSON_FILE_NAME); + if (!Files.exists(json)) { + final Path props = metaInfDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME); + if (Files.exists(props)) { + e = new JsonExtension(); + e.setArtifact(new ArtifactCoords(artifact.getGroupId(), artifact.getArtifactId(), + artifact.getClassifier(), artifact.getExtension(), artifact.getVersion())); + e.setName(artifact.getArtifactId()); } + } else { + e = processPlatformArtifact(artifact, json, mapper); } - return processPlatformArtifact(artifact, jsonOrYaml, mapper); + return e; } private JsonExtension processPlatformArtifact(Artifact artifact, Path descriptor, ObjectMapper mapper) diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/README.gradle.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/README.gradle.ftl deleted file mode 100644 index 7c9ddaef5f1659..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/README.gradle.ftl +++ /dev/null @@ -1,35 +0,0 @@ -# ${project_artifactId} project - -This project uses Quarkus, the Supersonic Subatomic Java Framework. - -If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . - -## Running the application in dev mode - -You can run your application in dev mode that enables live coding using: -``` -./gradlew quarkusDev -``` - -## Packaging and running the application - -The application can be packaged using `./gradlew quarkusBuild`. -It produces the `${project_artifactId}-${project_version}-runner.jar` file in the `build` directory. -Be aware that it’s not an _über-jar_ as the dependencies are copied into the `build/lib` directory. - -The application is now runnable using `java -jar build/${project_artifactId}-${project_version}-runner.jar`. - -If you want to build an _über-jar_, just add the `--uber-jar` option to the command line: -``` -./gradlew quarkusBuild --uber-jar -``` - -## Creating a native executable - -You can create a native executable using: `./gradlew build -Dquarkus.package.type=native`. - -Or, if you don't have GraalVM installed, you can run the native executable build in a container using: `./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true`. - -You can then execute your native executable with: `./build/${project_artifactId}-${project_version}-runner` - -If you want to learn more about building native executables, please consult https://quarkus.io/guides/gradle-tooling#building-a-native-executable. diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/README.maven.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/README.maven.ftl deleted file mode 100644 index 286dfb6c274850..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/README.maven.ftl +++ /dev/null @@ -1,30 +0,0 @@ -# ${project_artifactId} project - -This project uses Quarkus, the Supersonic Subatomic Java Framework. - -If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . - -## Running the application in dev mode - -You can run your application in dev mode that enables live coding using: -``` -./mvnw quarkus:dev -``` - -## Packaging and running the application - -The application can be packaged using `./mvnw package`. -It produces the `${project_artifactId}-${project_version}-runner.jar` file in the `/target` directory. -Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. - -The application is now runnable using `java -jar target/${project_artifactId}-${project_version}-runner.jar`. - -## Creating a native executable - -You can create a native executable using: `./mvnw package -Pnative`. - -Or, if you don't have GraalVM installed, you can run the native executable build in a container using: `./mvnw package -Pnative -Dquarkus.native.container-build=true`. - -You can then execute your native executable with: `./target/${project_artifactId}-${project_version}-runner` - -If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image. diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/build.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/build.gradle-template.ftl deleted file mode 100644 index b0ca2b031be0b9..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/build.gradle-template.ftl +++ /dev/null @@ -1,35 +0,0 @@ -plugins { - id 'java' - id 'io.quarkus' -} - -repositories { - mavenLocal() - mavenCentral()${maven_repositories} -} - -dependencies { - implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") - implementation 'io.quarkus:quarkus-resteasy' - - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:rest-assured' -} - -group '${project_groupId}' -version '${project_version}' - -compileJava { - options.encoding = 'UTF-8' - options.compilerArgs << '-parameters' -} - -compileTestJava { - options.encoding = 'UTF-8' -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/gradle.properties-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/gradle.properties-template.ftl deleted file mode 100644 index c89e173e9ad974..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/gradle.properties-template.ftl +++ /dev/null @@ -1,5 +0,0 @@ -quarkusPlatformGroupId = ${bom_groupId} -quarkusPlatformArtifactId = ${bom_artifactId} -quarkusPlatformVersion = ${bom_version} -quarkusPluginVersion = ${gradle_plugin_version} -org.gradle.logging.level=INFO diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/settings.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/settings.gradle-template.ftl deleted file mode 100644 index 61fd4debce633f..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/settings.gradle-template.ftl +++ /dev/null @@ -1,11 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - mavenCentral() - gradlePluginPortal()${maven_plugin_repositories} - } - plugins { - id 'io.quarkus' version "${quarkusPluginVersion}" - } -} -rootProject.name='${project_artifactId}' diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl deleted file mode 100644 index cc291567c90264..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl +++ /dev/null @@ -1,16 +0,0 @@ -package ${package_name}; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -@RestController -@RequestMapping("${path}") -public class ${class_name} { - - @GetMapping - public String hello() { - return "hello"; - } -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/build.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/build.gradle-template.ftl deleted file mode 100644 index f1cec0a5afe2dd..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/build.gradle-template.ftl +++ /dev/null @@ -1,50 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version "${kotlin_version}" - id "org.jetbrains.kotlin.plugin.allopen" version "${kotlin_version}" - id 'io.quarkus' -} - -repositories { - mavenLocal() - mavenCentral()${maven_repositories} -} - -dependencies { - implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") - implementation 'io.quarkus:quarkus-resteasy' - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:kotlin-extensions' -} - -group '${project_groupId}' -version '${project_version}' - -quarkus { - setOutputDirectory("$projectDir/build/classes/kotlin/main") -} - -quarkusDev { - setSourceDir("$projectDir/src/main/kotlin") -} - -allOpen { - annotation("javax.ws.rs.Path") - annotation("javax.enterprise.context.ApplicationScoped") - annotation("io.quarkus.test.junit.QuarkusTest") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -compileKotlin { - kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8 - kotlinOptions.javaParameters = true -} - -compileTestKotlin { - kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8 -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/gradle.properties-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/gradle.properties-template.ftl deleted file mode 100644 index c89e173e9ad974..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/gradle.properties-template.ftl +++ /dev/null @@ -1,5 +0,0 @@ -quarkusPlatformGroupId = ${bom_groupId} -quarkusPlatformArtifactId = ${bom_artifactId} -quarkusPlatformVersion = ${bom_version} -quarkusPluginVersion = ${gradle_plugin_version} -org.gradle.logging.level=INFO diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/native-test-resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/native-test-resource-template.ftl deleted file mode 100644 index bea133eec2aaa1..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/native-test-resource-template.ftl +++ /dev/null @@ -1,6 +0,0 @@ -package ${package_name} - -import io.quarkus.test.junit.NativeImageTest - -@NativeImageTest -class Native${class_name}IT : ${class_name}Test() diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/pom.xml-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/pom.xml-template.ftl deleted file mode 100644 index 8132da629b7470..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/pom.xml-template.ftl +++ /dev/null @@ -1,176 +0,0 @@ - - 4.0.0 - ${project_groupId} - ${project_artifactId} - ${project_version} - - - UTF-8 - UTF-8 - 1.8 - 1.8 - true - - ${bom_artifactId} - ${bom_groupId} - ${bom_version} - ${maven_plugin_version} - ${compiler_plugin_version} - ${surefire_plugin_version} - ${kotlin_version} - - ${maven_repositories}${maven_plugin_repositories} - - - - ${quarkus.platform.group-id} - ${quarkus.platform.artifact-id} - ${quarkus.platform.version} - pom - import - - - - - - - io.quarkus - quarkus-resteasy - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - kotlin-extensions - test - - - - - src/main/kotlin - src/test/kotlin - - - ${maven_plugin_groupId} - ${maven_plugin_artifactId} - ${quarkus-plugin.version} - - - - generate-code - generate-code-tests - build - - - - - - maven-compiler-plugin - ${compiler-plugin.version} - - - org.apache.maven.plugins - maven-surefire-plugin - ${surefire-plugin.version} - - - org.jboss.logmanager.LogManager - ${maven.home} - - - - - kotlin-maven-plugin - org.jetbrains.kotlin - ${kotlin.version} - - - compile - - compile - - - - test-compile - - test-compile - - - - - true - 1.8 - - - all-open - - - - - - - - - - - - - org.jetbrains.kotlin - kotlin-maven-allopen - ${kotlin.version} - - - - - - - - - native - - - native - - - - native - - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${surefire-plugin.version} - - - - integration-test - verify - - - - ${project.build.directory}/${project.build.finalName}-runner - org.jboss.logmanager.LogManager - ${maven.home} - - - - - - - - - - - diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/resource-template.ftl deleted file mode 100644 index f2e69a6a795b62..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/resource-template.ftl +++ /dev/null @@ -1,14 +0,0 @@ -package ${package_name} - -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - -@Path("${path}") -class ${class_name} { - - @GET - @Produces(MediaType.TEXT_PLAIN) - fun hello() = "hello" -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/settings.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/settings.gradle-template.ftl deleted file mode 100644 index 61fd4debce633f..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/settings.gradle-template.ftl +++ /dev/null @@ -1,11 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - mavenCentral() - gradlePluginPortal()${maven_plugin_repositories} - } - plugins { - id 'io.quarkus' version "${quarkusPluginVersion}" - } -} -rootProject.name='${project_artifactId}' diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/spring-controller-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/spring-controller-template.ftl deleted file mode 100644 index 29bdeabb92bd43..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/spring-controller-template.ftl +++ /dev/null @@ -1,14 +0,0 @@ -package ${package_name}; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -@RestController -@RequestMapping("${path}") -class ${class_name} { - - @GetMapping - fun hello() = "hello" -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/test-resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/test-resource-template.ftl deleted file mode 100644 index d31002cc1e2734..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/test-resource-template.ftl +++ /dev/null @@ -1,20 +0,0 @@ -package ${package_name} - -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.hamcrest.CoreMatchers.`is` -import org.junit.jupiter.api.Test - -@QuarkusTest -class ${class_name}Test { - - @Test - fun testHelloEndpoint() { - given() - .`when`().get("${path}") - .then() - .statusCode(200) - .body(`is`("hello")) - } - -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/build.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/build.gradle-template.ftl deleted file mode 100644 index 73ebc301f419ee..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/build.gradle-template.ftl +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'scala' - id 'io.quarkus' -} - -repositories { - mavenLocal() - mavenCentral()${maven_repositories} -} - -dependencies { - implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") - implementation 'org.scala-lang:scala-library:${scala_version}' - implementation 'io.quarkus:quarkus-resteasy' - - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:rest-assured' -} - -group '${project_groupId}' -version '${project_version}' - -compileScala { - scalaCompileOptions.encoding = 'UTF-8' - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/gradle.properties-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/gradle.properties-template.ftl deleted file mode 100644 index c89e173e9ad974..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/gradle.properties-template.ftl +++ /dev/null @@ -1,5 +0,0 @@ -quarkusPlatformGroupId = ${bom_groupId} -quarkusPlatformArtifactId = ${bom_artifactId} -quarkusPlatformVersion = ${bom_version} -quarkusPluginVersion = ${gradle_plugin_version} -org.gradle.logging.level=INFO diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/native-test-resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/native-test-resource-template.ftl deleted file mode 100644 index ee89a757a96a74..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/native-test-resource-template.ftl +++ /dev/null @@ -1,6 +0,0 @@ -package ${package_name} - -import io.quarkus.test.junit.NativeImageTest - -@NativeImageTest -class Native${class_name}IT extends ${class_name}Test diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/pom.xml-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/pom.xml-template.ftl deleted file mode 100644 index d0fa54ab61adf4..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/pom.xml-template.ftl +++ /dev/null @@ -1,173 +0,0 @@ - - 4.0.0 - ${project_groupId} - ${project_artifactId} - ${project_version} - - - UTF-8 - UTF-8 - 1.8 - 1.8 - true - - ${bom_artifactId} - ${bom_groupId} - ${bom_version} - ${maven_plugin_version} - ${compiler_plugin_version} - ${surefire_plugin_version} - ${scala_version} - ${scala_plugin_version} - - ${maven_repositories}${maven_plugin_repositories} - - - - ${quarkus.platform.group-id} - ${quarkus.platform.artifact-id} - ${quarkus.platform.version} - pom - import - - - - - - - io.quarkus - quarkus-resteasy - - - org.scala-lang - scala-library - ${scala.version} - - - org.scala-lang - scala-reflect - ${scala.version} - - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - - - src/main/scala - src/test/scala - - - ${maven_plugin_groupId} - ${maven_plugin_artifactId} - ${quarkus-plugin.version} - - - - generate-code - generate-code-tests - build - - - - - - maven-compiler-plugin - ${compiler-plugin.version} - - - org.apache.maven.plugins - maven-surefire-plugin - ${surefire-plugin.version} - - - org.jboss.logmanager.LogManager - ${maven.home} - - - - - net.alchim31.maven - scala-maven-plugin - ${scala-maven-plugin.version} - - - scala-compile-first - process-resources - - add-source - compile - - - - scala-test-compile - process-test-resources - - add-source - testCompile - - - - - ${scala.version} - - -deprecation - -feature - -explaintypes - -target:jvm-1.8 - -Ypartial-unification - - - - - - - - - native - - - native - - - - native - - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${surefire-plugin.version} - - - - integration-test - verify - - - - ${project.build.directory}/${project.build.finalName}-runner - org.jboss.logmanager.LogManager - ${maven.home} - - - - - - - - - - - diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/resource-template.ftl deleted file mode 100644 index 898fcb73fa2530..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/resource-template.ftl +++ /dev/null @@ -1,12 +0,0 @@ -package ${package_name} - -import javax.ws.rs.{GET, Path, Produces} -import javax.ws.rs.core.MediaType - -@Path("${path}") -class ${class_name} { - - @GET - @Produces(Array[String](MediaType.TEXT_PLAIN)) - def hello() = "hello" -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/settings.gradle-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/settings.gradle-template.ftl deleted file mode 100644 index 61fd4debce633f..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/settings.gradle-template.ftl +++ /dev/null @@ -1,11 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - mavenCentral() - gradlePluginPortal()${maven_plugin_repositories} - } - plugins { - id 'io.quarkus' version "${quarkusPluginVersion}" - } -} -rootProject.name='${project_artifactId}' diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/spring-controller-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/spring-controller-template.ftl deleted file mode 100644 index a3623acc157114..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/spring-controller-template.ftl +++ /dev/null @@ -1,12 +0,0 @@ -package ${package_name}; - -import org.springframework.web.bind.annotation.{GetMapping, RequestMapping, RestController}; - - -@RestController -@RequestMapping(Array[String]("${path}")) -class ${class_name} { - - @GetMapping - def hello() = "hello" -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/test-resource-template.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/test-resource-template.ftl deleted file mode 100644 index eaceeebedaeffd..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/scala/test-resource-template.ftl +++ /dev/null @@ -1,20 +0,0 @@ -package ${package_name} - -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.hamcrest.CoreMatchers.`is` -import org.junit.jupiter.api.Test - -@QuarkusTest -class ${class_name}Test { - - @Test - def testHelloEndpoint(): Unit = { - given() - .`when`().get("${path}") - .then() - .statusCode(200) - .body(`is`("hello")) - } - -} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-jvm.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-jvm.ftl deleted file mode 100644 index 39a3e02582e03f..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-jvm.ftl +++ /dev/null @@ -1,57 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -Dquarkus.package.type=fast-jar -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.fast-jar -t quarkus/${project_artifactId}-fast-jar . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/${project_artifactId}-fast-jar -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/${project_artifactId}-fast-jar -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -# We make four distinct layers so if there are application changes the library layers can be re-used -COPY --chown=1001 ${build_dir}/quarkus-app/lib/ /deployments/lib/ -COPY --chown=1001 ${build_dir}/quarkus-app/*.jar /deployments/ -COPY --chown=1001 ${build_dir}/quarkus-app/app/ /deployments/app/ -COPY --chown=1001 ${build_dir}/quarkus-app/quarkus/ /deployments/quarkus/ - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-legacy-jar.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-legacy-jar.ftl deleted file mode 100644 index 704c5073d52cb9..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-legacy-jar.ftl +++ /dev/null @@ -1,54 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/${project_artifactId}-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/${project_artifactId}-jvm -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/${project_artifactId}-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY ${build_dir}/lib/* /deployments/lib/ -COPY ${build_dir}/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native-distroless.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native-distroless.ftl deleted file mode 100644 index 4ba08903ff84a4..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native-distroless.ftl +++ /dev/null @@ -1,23 +0,0 @@ -#### -# This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode -# -# Before building the container image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/${project_artifactId} . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/${project_artifactId} -# -### -FROM quay.io/quarkus/quarkus-distroless-image:1.0 -COPY ${build-dir}/*-runner /application - -EXPOSE 8080 -USER nonroot - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native.ftl deleted file mode 100644 index a9fe968e96d480..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/dockerfile-native.ftl +++ /dev/null @@ -1,27 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the container image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t quarkus/${project_artifactId} . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/${project_artifactId} -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 -WORKDIR /work/ -RUN chown 1001 /work \ - && chmod "g+rwX" /work \ - && chown 1001:root /work -COPY --chown=1001:root ${build_dir}/*-runner /work/application - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/dockerignore.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/dockerignore.ftl deleted file mode 100644 index 4d16dfdf3b08c0..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/dockerignore.ftl +++ /dev/null @@ -1,5 +0,0 @@ -* -!${build_dir}/*-runner -!${build_dir}/*-runner.jar -!${build_dir}/lib/* -!${build_dir}/quarkus-app/* diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl deleted file mode 100644 index 38db6efa816ad6..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl +++ /dev/null @@ -1,34 +0,0 @@ -# Eclipse -.project -.classpath -.settings/ -bin/ - -# IntelliJ -.idea -*.ipr -*.iml -*.iws - -# NetBeans -nb-configuration.xml - -# Visual Studio Code -.vscode -.factorypath - -# OSX -.DS_Store - -# Vim -*.swp -*.swo - -# patch -*.orig -*.rej - -# Local environment -.env - -${additional_gitignore_entries} diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/index.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/index.ftl deleted file mode 100644 index ef51dbc6ab16c3..00000000000000 --- a/devtools/platform-descriptor-json/src/main/resources/templates/index.ftl +++ /dev/null @@ -1,155 +0,0 @@ - - - - - ${project_artifactId} - ${project_version} - - - - -

- -
-
-

Congratulations, you have created a new Quarkus application.

- -

Why do you see this?

- -

This page is served by Quarkus. The source is in - src/main/resources/META-INF/resources/index.html.

- -

What can I do from here?

- -

If not already done, run the application in dev mode using: mvn compile quarkus:dev. -

-
    -
  • Add REST resources, Servlets, functions and other services in src/main/java.
  • -
  • Your static assets are located in src/main/resources/META-INF/resources.
  • -
  • Configure your application in src/main/resources/application.properties. -
  • -
- -

Do you like Quarkus?

-

Go give it a star on GitHub.

- -

How do I get rid of this page?

-

Just delete the src/main/resources/META-INF/resources/index.html file.

-
-
-
-

Application

-
    -
  • GroupId: ${project_groupId}
  • -
  • ArtifactId: ${project_artifactId}
  • -
  • Version: ${project_version}
  • -
  • Quarkus Version: ${quarkus_version}
  • -
-
- -
-
- - - - diff --git a/devtools/pom.xml b/devtools/pom.xml index ec3cd0a47327e8..236312274469c6 100644 --- a/devtools/pom.xml +++ b/devtools/pom.xml @@ -20,10 +20,10 @@ + project-core-extension-codestarts platform-descriptor-json-plugin platform-properties bom-descriptor-json - platform-descriptor-json maven gradle cli diff --git a/devtools/platform-descriptor-json/pom.xml b/devtools/project-core-extension-codestarts/pom.xml similarity index 52% rename from devtools/platform-descriptor-json/pom.xml rename to devtools/project-core-extension-codestarts/pom.xml index 269ae990b3ab77..ae731ad5282d55 100644 --- a/devtools/platform-descriptor-json/pom.xml +++ b/devtools/project-core-extension-codestarts/pom.xml @@ -10,8 +10,8 @@ 999-SNAPSHOT - quarkus-platform-descriptor-json - Quarkus - Dev tools - Platform Descriptor - JSON + quarkus-project-core-extension-codestarts + Quarkus - Dev tools - Project Core Extension Codestarts ${project.basedir}/../gradle @@ -29,48 +29,6 @@ - - maven-dependency-plugin - - - copy-bom - - copy - - - - - io.quarkus - quarkus-bom - ${project.version} - pom - ${project.build.outputDirectory}/quarkus-bom - pom.xml - - - - - - copy-bom-descriptor-json - - copy - - - - - io.quarkus - quarkus-bom-quarkus-platform-descriptor - ${project.version} - json - ${project.version} - ${project.build.outputDirectory}/quarkus-bom-descriptor - extensions.json - - - - - - org.codehaus.mojo exec-maven-plugin @@ -104,16 +62,6 @@ - - - io.quarkus - quarkus-bom-quarkus-platform-descriptor - ${project.version} - json - ${project.version} - - - windows diff --git a/devtools/platform-descriptor-json/src/main/filtered/quarkus.properties b/devtools/project-core-extension-codestarts/src/main/filtered/quarkus.properties similarity index 94% rename from devtools/platform-descriptor-json/src/main/filtered/quarkus.properties rename to devtools/project-core-extension-codestarts/src/main/filtered/quarkus.properties index 2c025575f58270..ef62a1553940f0 100644 --- a/devtools/platform-descriptor-json/src/main/filtered/quarkus.properties +++ b/devtools/project-core-extension-codestarts/src/main/filtered/quarkus.properties @@ -23,4 +23,4 @@ supported-maven-versions=${supported-maven-versions} # the proposed version must be in the range of the supported versions proposed-maven-version=${proposed-maven-version} maven-wrapper-version=${maven-wrapper.version} -gradle-wrapper-version=${gradle-wrapper.version} +gradle-wrapper-version=${gradle-wrapper.version} \ No newline at end of file diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{command.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{command.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{command.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{command.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/java/src/{resource.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/java/src/{resource.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/java/src/{resource.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/java/src/{resource.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/language/java/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/language/java/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/language/java/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/language/java/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang.cmd b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang.cmd similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang.cmd rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/base/jbang.cmd diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus-jbang/tooling/jbang-wrapper/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/..gitignore b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/..gitignore similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/..gitignore rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/..gitignore diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/settings.tpl.qute.gradle.kts b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/settings.tpl.qute.gradle.kts similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/settings.tpl.qute.gradle.kts rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/base/settings.tpl.qute.gradle.kts diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/java/build.tpl.qute.gradle.kts b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/java/build.tpl.qute.gradle.kts similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/java/build.tpl.qute.gradle.kts rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/java/build.tpl.qute.gradle.kts diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/scala/build.tpl.qute.gradle.kts b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/scala/build.tpl.qute.gradle.kts similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/scala/build.tpl.qute.gradle.kts rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle-kotlin-dsl/scala/build.tpl.qute.gradle.kts diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/..gitignore b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/..gitignore similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/..gitignore rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/..gitignore diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/build-layout.include.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/build-layout.include.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/build-layout.include.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/build-layout.include.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/gradle.tpl.qute.properties b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/gradle.tpl.qute.properties similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/gradle.tpl.qute.properties rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/gradle.tpl.qute.properties diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/settings.tpl.qute.gradle b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/settings.tpl.qute.gradle similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/settings.tpl.qute.gradle rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/base/settings.tpl.qute.gradle diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/java/build.tpl.qute.gradle b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/java/build.tpl.qute.gradle similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/java/build.tpl.qute.gradle rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/java/build.tpl.qute.gradle diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/kotlin/build.tpl.qute.gradle b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/kotlin/build.tpl.qute.gradle similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/kotlin/build.tpl.qute.gradle rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/kotlin/build.tpl.qute.gradle diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/scala/build.tpl.qute.gradle b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/scala/build.tpl.qute.gradle similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/gradle/scala/build.tpl.qute.gradle rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/gradle/scala/build.tpl.qute.gradle diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/..gitignore b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/..gitignore similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/..gitignore rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/..gitignore diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/pom.tpl.qute.xml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/pom.tpl.qute.xml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/pom.tpl.qute.xml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/base/pom.tpl.qute.xml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/java/.gitkeep b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/java/.gitkeep similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/java/.gitkeep rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/java/.gitkeep diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/kotlin/pom.tpl.qute.xml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/kotlin/pom.tpl.qute.xml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/kotlin/pom.tpl.qute.xml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/kotlin/pom.tpl.qute.xml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/scala/pom.tpl.qute.xml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/scala/pom.tpl.qute.xml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/buildtool/maven/scala/pom.tpl.qute.xml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/buildtool/maven/scala/pom.tpl.qute.xml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/config/properties/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/config/properties/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/config/properties/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/config/properties/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/config/yaml/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/config/yaml/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/config/yaml/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/config/yaml/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/java/src/main/java/org/acme/{main.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/java/src/main/java/org/acme/{main.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/java/src/main/java/org/acme/{main.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/java/src/main/java/org/acme/{main.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/kotlin/src/main/kotlin/org/acme/{main.class-name}.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/kotlin/src/main/kotlin/org/acme/{main.class-name}.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/kotlin/src/main/kotlin/org/acme/{main.class-name}.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/commandmode-example/kotlin/src/main/kotlin/org/acme/{main.class-name}.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/resteasy-reactive-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/main/java/org/acme/{resource.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/native-test/java/org/acme/Native{resource.class-name}IT.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/java/src/test/java/org/acme/{resource.class-name}Test.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/main/kotlin/org/acme/{resource.class-name}.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/native-test/kotlin/org/acme/Native{resource.class-name}IT.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/kotlin/src/test/kotlin/org/acme/{resource.class-name}Test.tpl.qute.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/main/scala/org/acme/{resource.class-name}.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/native-test/scala/org/acme/Native{resource.class-name}IT.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/examples/spring-web-example/scala/src/test/scala/org/acme/{resource.class-name}Test.tpl.qute.scala diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/java/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/java/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/java/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/java/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/kotlin/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/kotlin/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/kotlin/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/kotlin/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/scala/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/scala/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/language/scala/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/language/scala/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/index-entry.include.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/index-entry.include.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/index-entry.include.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/index-entry.include.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/readme-header.include.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/readme-header.include.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/readme-header.include.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/readme-header.include.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/META-INF/resources/index.tpl.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/META-INF/resources/index.tpl.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/META-INF/resources/index.tpl.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/META-INF/resources/index.tpl.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/application.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/application.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/application.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/base/src/main/resources/application.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/java/src/main/java/.gitkeep b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/java/src/main/java/.gitkeep similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/java/src/main/java/.gitkeep rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/java/src/main/java/.gitkeep diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/kotlin/src/main/kotlin/.gitkeep b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/kotlin/src/main/kotlin/.gitkeep similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/kotlin/src/main/kotlin/.gitkeep rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/kotlin/src/main/kotlin/.gitkeep diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/scala/src/main/scala/.gitkeep b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/scala/src/main/scala/.gitkeep similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/scala/src/main/scala/.gitkeep rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/project/quarkus/scala/src/main/scala/.gitkeep diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/.dockerignore.tpl.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/.dockerignore.tpl.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/.dockerignore.tpl.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/.dockerignore.tpl.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/Dockerfile-layout.include.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/Dockerfile-layout.include.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/Dockerfile-layout.include.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/Dockerfile-layout.include.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.jvm b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.jvm similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.jvm rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.jvm diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.legacy-jar b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.legacy-jar similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.legacy-jar rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.legacy-jar diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native-distroless b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native-distroless similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native-distroless rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/base/src/main/docker/Dockerfile.tpl.qute.native-distroless diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/dockerfiles/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/github-action/base/.github/workflows/ci.tpl.qute.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/github-action/base/.github/workflows/ci.tpl.qute.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/github-action/base/.github/workflows/ci.tpl.qute.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/github-action/base/.github/workflows/ci.tpl.qute.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/github-action/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/github-action/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/github-action/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/github-action/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.jar b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.jar rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.jar diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.tpl.qute.properties b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.tpl.qute.properties similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.tpl.qute.properties rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradle/wrapper/gradle-wrapper.tpl.qute.properties diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew.bat b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew.bat similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew.bat rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/base/gradlew.bat diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/gradle-wrapper/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/MavenWrapperDownloader.tpl.qute.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/MavenWrapperDownloader.tpl.qute.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/MavenWrapperDownloader.tpl.qute.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/MavenWrapperDownloader.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/maven-wrapper.tpl.qute.properties b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/maven-wrapper.tpl.qute.properties similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/maven-wrapper.tpl.qute.properties rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/.mvn/wrapper/maven-wrapper.tpl.qute.properties diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute.cmd b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute.cmd similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute.cmd rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/base/mvnw.tpl.qute.cmd diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/core/tooling/maven-wrapper/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/application.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/application.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/application.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/base/src/main/resources/application.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/java/src/main/java/org/acme/config/ConfigResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/java/src/main/java/org/acme/config/ConfigResource.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/java/src/main/java/org/acme/config/ConfigResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/java/src/main/java/org/acme/config/ConfigResource.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/kotlin/src/main/kotlin/org/acme/config/ConfigResource.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/kotlin/src/main/kotlin/org/acme/config/ConfigResource.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/config-example/kotlin/src/main/kotlin/org/acme/config/ConfigResource.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/config-example/kotlin/src/main/kotlin/org/acme/config/ConfigResource.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/main/java/org/acme/funqy/Funqy.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/main/java/org/acme/funqy/Funqy.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/main/java/org/acme/funqy/Funqy.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/main/java/org/acme/funqy/Funqy.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/test/java/org/acme/funqy/FunqyTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/test/java/org/acme/funqy/FunqyTest.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/test/java/org/acme/funqy/FunqyTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-http-example/java/src/test/java/org/acme/funqy/FunqyTest.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/application.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/application.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/application.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/base/src/main/resources/application.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/java/src/main/java/org/acme/logging/json/LoggingJsonResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/java/src/main/java/org/acme/logging/json/LoggingJsonResource.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/java/src/main/java/org/acme/logging/json/LoggingJsonResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/java/src/main/java/org/acme/logging/json/LoggingJsonResource.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/kotlin/src/main/kotlin/org/acme/logging/json/LoggingJsonResource.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/kotlin/src/main/kotlin/org/acme/logging/json/LoggingJsonResource.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/logging-json/kotlin/src/main/kotlin/org/acme/logging/json/LoggingJsonResource.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/logging-json/kotlin/src/main/kotlin/org/acme/logging/json/LoggingJsonResource.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GreetingService.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GreetingService.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GreetingService.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GreetingService.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/HelloCommand.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/HelloCommand.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/HelloCommand.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/HelloCommand.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/EntryCommand.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/EntryCommand.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/EntryCommand.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/EntryCommand.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GoodbyeCommand.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GoodbyeCommand.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GoodbyeCommand.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GoodbyeCommand.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GreetingService.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GreetingService.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GreetingService.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GreetingService.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/HelloCommand.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/HelloCommand.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/HelloCommand.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/HelloCommand.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/JacksonResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/JacksonResource.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/JacksonResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/JacksonResource.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/MyObjectMapperCustomizer.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/MyObjectMapperCustomizer.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/MyObjectMapperCustomizer.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/resteasyjackson/MyObjectMapperCustomizer.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/JacksonResource.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/JacksonResource.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/JacksonResource.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/JacksonResource.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/MyObjectMapperCustomizer.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/MyObjectMapperCustomizer.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/MyObjectMapperCustomizer.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/kotlin/src/main/kotlin/org/acme/resteasyjackson/MyObjectMapperCustomizer.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/templates/page.qute.tpl.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/templates/page.qute.tpl.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/templates/page.qute.tpl.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/base/src/main/resources/templates/page.qute.tpl.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/Quark.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/Quark.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/Quark.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/Quark.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/QuteResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/QuteResource.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/QuteResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/java/src/main/java/org/acme/resteasyqute/QuteResource.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/Quark.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/Quark.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/Quark.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/Quark.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/QuteResource.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/QuteResource.kt similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/QuteResource.kt rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/resteasy-qute-example/kotlin/src/main/kotlin/org/acme/resteasyqute/QuteResource.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/payload.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/payload.json similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/payload.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/payload.json diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/GreetingLambda.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/GreetingLambda.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/GreetingLambda.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/GreetingLambda.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/Person.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/Person.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/Person.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/main/java/org/acme/lambda/Person.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/native-test/java/org/acme/lambda/LambdaHandlerTestIT.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/native-test/java/org/acme/lambda/LambdaHandlerTestIT.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/native-test/java/org/acme/lambda/LambdaHandlerTestIT.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/native-test/java/org/acme/lambda/LambdaHandlerTestIT.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/java/org/acme/lambda/LambdaHandlerTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/java/org/acme/lambda/LambdaHandlerTest.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/java/org/acme/lambda/LambdaHandlerTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/java/org/acme/lambda/LambdaHandlerTest.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/resources/application.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/resources/application.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/resources/application.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/amazon-lambda-example/java/src/test/resources/application.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/function.tpl.qute.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/function.tpl.qute.json similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/function.tpl.qute.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/function.tpl.qute.json diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/host.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/host.json similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/host.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/host.json diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/local.settings.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/local.settings.json similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/local.settings.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/azure-config/local.settings.json diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/pom.tpl.qute.xml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/pom.tpl.qute.xml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/pom.tpl.qute.xml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/pom.tpl.qute.xml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/azure-functions-http-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/payload.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/payload.json similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/payload.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/payload.json diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/GreetingFunction.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/GreetingFunction.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/GreetingFunction.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/GreetingFunction.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/Person.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/Person.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/Person.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/main/java/org/acme/funqy/Person.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/org/acme/funqy/FunqyIT.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/resources/application.properties b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/resources/application.properties similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/resources/application.properties rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/native-test/java/resources/application.properties diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/java/org/acme/funqy/FunqyTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/java/org/acme/funqy/FunqyTest.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/java/org/acme/funqy/FunqyTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/java/org/acme/funqy/FunqyTest.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/resources/application.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/resources/application.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/resources/application.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-amazon-lambda-example/java/src/test/resources/application.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingFunctions.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingFunctions.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingFunctions.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingFunctions.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingService.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingService.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingService.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-google-cloud-functions-example/java/src/main/java/org/acme/funqygooglecloudfunctions/GreetingService.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/CloudEventGreeting.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/CloudEventGreeting.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/CloudEventGreeting.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/CloudEventGreeting.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/Person.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/Person.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/Person.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/java/org/acme/funqy/cloudevent/Person.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-service.tpl.qute.yaml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-service.tpl.qute.yaml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-service.tpl.qute.yaml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-service.tpl.qute.yaml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-trigger.tpl.qute.yaml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-trigger.tpl.qute.yaml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-trigger.tpl.qute.yaml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/main/k8s/funqy-trigger.tpl.qute.yaml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/native-test/java/org/acme/funqy/cloudevent/FunqyIT.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/native-test/java/org/acme/funqy/cloudevent/FunqyIT.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/native-test/java/org/acme/funqy/cloudevent/FunqyIT.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/native-test/java/org/acme/funqy/cloudevent/FunqyIT.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/test/java/org/acme/funqy/cloudevent/FunqyTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/test/java/org/acme/funqy/cloudevent/FunqyTest.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/test/java/org/acme/funqy/cloudevent/FunqyTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/funqy-knative-events-example/java/src/test/java/org/acme/funqy/cloudevent/FunqyTest.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldBackgroundFunction.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldBackgroundFunction.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldBackgroundFunction.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldBackgroundFunction.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldHttpFunction.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldHttpFunction.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldHttpFunction.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-example/java/src/main/java/org/acme/googlecloudfunctions/HelloWorldHttpFunction.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/base/README.tpl.qute.md similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/base/README.tpl.qute.md diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/codestart.yml similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/codestart.yml diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingFunqy.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingFunqy.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingFunqy.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingFunqy.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingResource.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingResource.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingRoutes.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingRoutes.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingRoutes.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingRoutes.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingServlet.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingServlet.java similarity index 100% rename from devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingServlet.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/singleton-examples/google-cloud-functions-http-example/java/src/main/java/org/acme/googlecloudfunctions/GreetingServlet.java diff --git a/docs/pom.xml b/docs/pom.xml index fabc2a5f77a649..9f8cb2957af0cd 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -253,6 +253,19 @@
+ + io.quarkus + quarkus-apache-httpclient-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-arc-deployment @@ -1176,6 +1189,19 @@ + + io.quarkus + quarkus-kubernetes-client-internal-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-kubernetes-config-deployment @@ -1540,6 +1566,32 @@ + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-picocli-deployment @@ -1735,6 +1787,19 @@ + + io.quarkus + quarkus-rest-client-reactive-jackson-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy-deployment @@ -1891,6 +1956,19 @@ + + io.quarkus + quarkus-resteasy-reactive-jackson-common-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy-reactive-jsonb-deployment @@ -1904,6 +1982,19 @@ + + io.quarkus + quarkus-resteasy-reactive-links-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-resteasy-reactive-qute-deployment diff --git a/docs/src/main/asciidoc/amazon-dynamodb.adoc b/docs/src/main/asciidoc/amazon-dynamodb.adoc index 236d9504d433a0..a0767268d91c8b 100644 --- a/docs/src/main/asciidoc/amazon-dynamodb.adoc +++ b/docs/src/main/asciidoc/amazon-dynamodb.adoc @@ -341,6 +341,10 @@ And add following dependency to the application `pom.xml`: software.amazon.awssdk apache-client + + io.quarkus + quarkus-apache-httpclient + ---- If you're going to use a local DynamoDB instance, configure it as follows: diff --git a/docs/src/main/asciidoc/amazon-kms.adoc b/docs/src/main/asciidoc/amazon-kms.adoc index 1cb3f0a8f37aec..27a1190d7d93ec 100644 --- a/docs/src/main/asciidoc/amazon-kms.adoc +++ b/docs/src/main/asciidoc/amazon-kms.adoc @@ -186,6 +186,10 @@ And add the following dependency to the application `pom.xml`: software.amazon.awssdk apache-client
+ + io.quarkus + quarkus-apache-httpclient + ---- If you're going to use a local KMS instance, configure it as follows: diff --git a/docs/src/main/asciidoc/amazon-s3.adoc b/docs/src/main/asciidoc/amazon-s3.adoc index c7f313dd637ade..5226e32b18471c 100644 --- a/docs/src/main/asciidoc/amazon-s3.adoc +++ b/docs/src/main/asciidoc/amazon-s3.adoc @@ -372,6 +372,10 @@ And add following dependency to the application `pom.xml`: software.amazon.awssdk apache-client + + io.quarkus + quarkus-apache-httpclient + ---- For asynchronous client refer to <> for more information. diff --git a/docs/src/main/asciidoc/amazon-ses.adoc b/docs/src/main/asciidoc/amazon-ses.adoc index 3d99ed022ef45c..163af2c6383ab4 100644 --- a/docs/src/main/asciidoc/amazon-ses.adoc +++ b/docs/src/main/asciidoc/amazon-ses.adoc @@ -178,6 +178,10 @@ And add the following dependency to the application `pom.xml`: software.amazon.awssdk apache-client + + io.quarkus + quarkus-apache-httpclient + ---- If you're going to use a local SES instance, configure it as follows: diff --git a/docs/src/main/asciidoc/amazon-sns.adoc b/docs/src/main/asciidoc/amazon-sns.adoc index c4bfbd6931c7a8..20e48bea505342 100644 --- a/docs/src/main/asciidoc/amazon-sns.adoc +++ b/docs/src/main/asciidoc/amazon-sns.adoc @@ -611,6 +611,10 @@ And add the following dependency to the application `pom.xml`: software.amazon.awssdk apache-client + + io.quarkus + quarkus-apache-httpclient + ---- If you're going to use a local SNS instance, configure it as follows: diff --git a/docs/src/main/asciidoc/amazon-sqs.adoc b/docs/src/main/asciidoc/amazon-sqs.adoc index 2be94580abc44b..7edb5b12726db4 100644 --- a/docs/src/main/asciidoc/amazon-sqs.adoc +++ b/docs/src/main/asciidoc/amazon-sqs.adoc @@ -293,6 +293,10 @@ And add the following dependency to the application `pom.xml`: software.amazon.awssdk apache-client + + io.quarkus + quarkus-apache-httpclient + ---- If you're going to use a local SQS instance, configure it as follows: diff --git a/docs/src/main/asciidoc/amqp.adoc b/docs/src/main/asciidoc/amqp.adoc index 804ae4b19a49ce..071272d460f8a0 100644 --- a/docs/src/main/asciidoc/amqp.adoc +++ b/docs/src/main/asciidoc/amqp.adoc @@ -8,7 +8,7 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize MicroProfile Reactive Messaging to interact with AMQP. +This guide demonstrates how your Quarkus application can utilize SmallRye Reactive Messaging to interact with AMQP. include::./status-include.adoc[] @@ -357,7 +357,7 @@ The new `Emitter.send` method returns a `CompletionStage` completed when the pro == Going further This guide has shown how you can interact with AMQP using Quarkus. -It utilizes MicroProfile Reactive Messaging to build data streaming applications. +It utilizes SmallRye Reactive Messaging to build data streaming applications. If you did the Kafka quickstart, you have realized that it's the same code. The only difference is the connector configuration. diff --git a/docs/src/main/asciidoc/building-native-image.adoc b/docs/src/main/asciidoc/building-native-image.adoc index 9d550738ae7c5c..aaf137710f5ab3 100644 --- a/docs/src/main/asciidoc/building-native-image.adoc +++ b/docs/src/main/asciidoc/building-native-image.adoc @@ -494,7 +494,7 @@ And finally, run it with: docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started ---- -NOTE: If you are interested in tiny Docker images, check the {quarkus-images-url}/master/distroless[distroless] version. +NOTE: If you are interested in tiny Docker images, check the {quarkus-images-url}/main/distroless[distroless] version. === Using a multi-stage Docker build @@ -540,7 +540,7 @@ Sample Dockerfile for building with Gradle: [source,dockerfile,subs=attributes+] ---- ## Stage 1 : build with maven builder image with native capabilities -FROM quay.io/quarkus/centos-quarkus-maven:{graalvm-version}-java8 AS build +FROM quay.io/quarkus/centos-quarkus-maven:{graalvm-flavor} AS build COPY src /usr/src/app/src COPY build.gradle /usr/src/app COPY settings.gradle /usr/src/app diff --git a/docs/src/main/asciidoc/cassandra.adoc b/docs/src/main/asciidoc/cassandra.adoc index dddcc695b8aaa6..b9dcfe75c4d63e 100644 --- a/docs/src/main/asciidoc/cassandra.adoc +++ b/docs/src/main/asciidoc/cassandra.adoc @@ -17,95 +17,93 @@ include::./platform-include.adoc[] == Prerequisites -To complete this guide, you need: - -* an IDE -* JDK 1.8+ installed with `JAVA_HOME` configured appropriately -* GraalVM installed with `GRAALVM_HOME` configured appropriately if you want to use the native mode. -* Apache Maven {maven-version} -* Cassandra or Docker installed +To complete this quickstart guide, you need: + +* an IDE; +* JDK 1.8+ installed with `JAVA_HOME` configured appropriately; +* link:https://www.graalvm.org/[GraalVM] installed with the `GRAALVM_HOME` environment variable +configured appropriately, if you want to +link:https://quarkus.io/guides/building-native-image[use the native mode]; +* Apache Maven 3.5.3+; +* A running link:https://cassandra.apache.org[Apache Cassandra], +link:https://www.datastax.fr/products/datastax-enterprise[DataStax Enterprise] (DSE) or +link:https://astra.datastax.com[DataStax Astra] database; or alternatively, a fresh Docker +installation. == Architecture -The application built in this guide is quite simple: the user can add elements in a list using a -form, and the items list is updated. +This quickstart guide shows how to build a REST application using the +link:https://github.com/datastax/cassandra-quarkus[Cassandra Quarkus extension], which allows you to +connect to an Apache Cassandra, DataStax Enterprise (DSE) or DataStax Astra database, using the +link:https://docs.datastax.com/en/developer/java-driver/latest[DataStax Java driver]. -All the information between the browser and the server is formatted as JSON. +This guide will also use the +link:https://docs.datastax.com/en/developer/java-driver/latest/manual/mapper[DataStax Object Mapper] +– a powerful Java-to-CQL mapping framework that greatly simplifies your application's data access +layer code by sparing you the hassle of writing your CQL queries by hand. -The elements are stored in the Cassandra database. +The application built in this quickstart guide is quite simple: the user can add elements in a list +using a form, and the items list is updated. All the information between the browser and the server +is formatted as JSON, and the elements are stored in the Cassandra database. == Solution We recommend that you follow the instructions in the next sections and create the application step -by step. -However, you can go right to the completed example. +by step. However, you can go right to the completed example. -The solution is located in the `quickstart` -link:https://github.com/datastax/cassandra-quarkus/tree/master/quickstart[directory]. +The solution is located in the +link:https://github.com/datastax/cassandra-quarkus/tree/master/quickstart[quickstart directory] of +the Cassandra Quarkus extension GitHub repository. -== Creating the Maven project +== Creating a Blank Maven Project First, create a new Maven project and copy the `pom.xml` file that is present in the `quickstart` directory. -The `pom.xml` is importing the RESTEasy/JAX-RS, JSON-B, Context Propagation and Cassandra Client -extensions. - -We will be building a REST application using the -link:https://docs.datastax.com/en/developer/java-driver/latest/manual/mapper[DataStax Object Mapper] -to simplify the Data Access Layer code. - -The most important part of the `pom.xml` is adding the `cassandra-quarkus` extension: - -[source,xml] ----- - - com.datastax.oss.quarkus - cassandra-quarkus-client - ${quarkus.version} - ----- - -Also make sure to follow the -link:https://docs.datastax.com/en/developer/java-driver/latest/manual/mapper/config/[instructions] -on how to add an annotation processor to the compiler configuration. When the project is compiled, -additional mapper classes are generated. +The `pom.xml` is importing all the Quarkus extensions and dependencies you need. -== Creating JSON REST service +== Creating the Data Model and Data Access Objects In this example, we will create an application to manage a list of fruits. -First, let's create the `Fruit` bean as follows: +First, let's create our data model – represented by the `Fruit` class – as follows: [source,java] ---- @Entity +@PropertyStrategy(mutable = false) public class Fruit { - @PartitionKey private String storeId; - @ClusteringColumn private String name; - private String description; + @PartitionKey + private final String name; - public Fruit() {} + private final String description; - public Fruit(String storeId, String name, String description) { - this.storeId = storeId; + public Fruit(String name, String description) { this.name = name; this.description = description; } - - // getters, setters, hashCode and equals omitted for brevity + // getters, hashCode, equals, toString methods omitted for brevity } ---- -We are using DataStax Java driver Object Mapper, which is why this class is annotated with an -`@Entity`. Also, the `storeId` field represents a Cassandra partition key and `name` represents a -clustering column, and so we are using the corresponding annotations from the Object Mapper library. -It will allow the Mapper to generate proper CQL queries underneath. +As stated above, we are using the DataStax Object Mapper. In other words, we are not going to write +our CQL queries manually; instead, we will annotate our data model with a few annotations, and the +mapper will generate proper CQL queries underneath. + +This is why the `Fruit` class is annotated with `@Entity`: this annotation marks it as an _entity +class_ that is mapped to a Cassandra table. Its instances are meant to be automatically persisted +into, and retrieved from, the Cassandra database. Here, the table name will be inferred from the +class name: `fruit`. + +Also, the `name` field represents a Cassandra partition key, and so we are annotating it with +`@PartitionKey` – another annotation from the Object Mapper library. -IMPORTANT: Entity classes are required to have a default no-args constructor. +IMPORTANT: Entity classes are normally required to have a default no-arg constructor, unless they +are annotated with `@PropertyStrategy(mutable = false)`, which is the case here. -To leverage the Mapper logic in this app we need to create a DAO: +The next step is to create a DAO (Data Access Object) interface that will manage instances of +`Fruit` entities: [source,java] ---- @@ -115,13 +113,19 @@ public interface FruitDao { void update(Fruit fruit); @Select - PagingIterable findById(String id); + PagingIterable findAll(); } ---- -This class exposes operations that will be used in the REST service. +This interface exposes operations that will be used in our REST service. Again, the annotation +`@Dao` comes from the DataStax Object Mapper, which will also automatically generate an +implementation of this interface for you. -Finally, the Mapper itself: +Note also the special return type of the `findAll` method, +link:https://docs.datastax.com/en/drivers/java/latest/com/datastax/oss/driver/api/core/PagingIterable.html[`PagingIterable`]: +it's the base type of result sets returned by the driver. + +Finally, let's create the a Mapper interface: [source,java] ---- @@ -132,128 +136,88 @@ public interface FruitMapper { } ---- -The mapper is responsible for constructing instances of `FruitDao`. In the example above, the -`FruitDao` instance will be connected to the same keyspace as the underlying session. More on that -below. - -TIP: It is also possible to create DAO instances for different keyspaces. To learn how, see -link:https://docs.datastax.com/en/developer/java-driver/4.7/manual/mapper/mapper/#dao-parameterization[DAO parameterization] -in the driver docs. - -Next, we need a component to create our DAO instances: `FruitDaoProducer`. Indeed, Mapper and Dao -instances are stateful objects, and should be created only once, as application-scoped singletons. -This component will do exactly that, leveraging Quarkus Dependency Injection container: - -[source, java] ----- -import com.datastax.oss.driver.api.core.CqlIdentifier; -import com.datastax.oss.quarkus.runtime.api.config.CassandraClientConfig; -import com.datastax.oss.quarkus.runtime.api.session.QuarkusCqlSession; -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; -import javax.inject.Inject; - -public class FruitDaoProducer { - - private final FruitDao fruitDao; - private final FruitDaoReactive fruitDaoReactive; - - @Inject - public FruitDaoProducer(QuarkusCqlSession session) { - // create a mapper - FruitMapper mapper = new FruitMapperBuilder(session).build(); - // instantiate our Daos - fruitDao = mapper.fruitDao(); - fruitDaoReactive = mapper.fruitDaoReactive(); - } - - @Produces - @ApplicationScoped - FruitDao produceFruitDao() { - return fruitDao; - } - - @Produces - @ApplicationScoped - FruitDaoReactive produceFruitDaoReactive() { - return fruitDaoReactive; - } -} ----- - -Note how the `QuarkusCqlSession` instance is injected automatically by the cassandra-quarkus -extension in the `FruitDaoProducer` constructor. +The `@Mapper` annotation is yet another annotation recognized by the DataStax Object Mapper. A +mapper is responsible for constructing instances of DAOs – in this case, out mapper is constructing +an instance of our only DAO, `FruitDao`. -Also note that `FruitMapperBuilder` is one of the classes generated automatically by the -`java-driver-mapper-processor` annotation processor. +== Creating a Service & JSON REST Endpoint -Now create a `FruitService` that will be the business layer of our application and store/load the -fruits from the Cassandra database. +Now let's create a `FruitService` that will be the business layer of our application and store/load +the fruits from the Cassandra database. [source,java] ---- @ApplicationScoped public class FruitService { - private final FruitDao dao; - - @Inject - public FruitService(FruitDao dao) { - this.dao = dao; - } + @Inject FruitDao dao; public void save(Fruit fruit) { dao.update(fruit); } - public List get(String id) { - return dao.findById(id).all(); + public List getAll() { + return dao.findAll().all(); } } ---- -Note how the service receives a `FruitDao` instance in the constructor. This DAO instance is -provided by `FruitDaoProducer` and injected automatically. +Note how the service is being injected a `FruitDao` instance. This DAO instance is injected +automatically. + +The Cassandra Quarkus extension allows you to inject any of the following beans in your own +components: + +- All `@Mapper`-annotated interfaces in your project. +- All `@Dao`-annotated interfaces in your project, as long as they are produced by a corresponding +`@DaoFactory`-annotated method declared in a mapper interface from your project. +- The +link:https://javadoc.io/doc/com.datastax.oss.quarkus/cassandra-quarkus-client/latest/com/datastax/oss/quarkus/runtime/api/session/QuarkusCqlSession.html[`QuarkusCqlSession`] +bean: this application-scoped, singleton bean is your main entry point to the Cassandra client; it +is a specialized Cassandra driver session instance with a few methods tailored especially for +Quarkus. Read its javadocs carefully! + +In our example, both `FruitMapper` and `FruitDao` could be injected anywhere. We chose to inject +`FruitDao` in `FruitService`. The last missing piece is the REST API that will expose GET and POST methods: [source,java] ---- @Path("/fruits") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) public class FruitResource { - private static final String STORE_NAME = "acme"; - @Inject FruitService fruitService; @GET - public List list() { - return fruitService.get(STORE_NAME).stream() - .map(fruit -> new FruitDto(fruit.getName(), fruit.getDescription())) - .collect(Collectors.toList()); + public List getAll() { + return fruitService.getAll().stream().map(this::convertToDto).collect(Collectors.toList()); } @POST public void add(FruitDto fruit) { - fruitService.save(covertFromDto(fruit)); + fruitService.save(convertFromDto(fruit)); + } + + private FruitDto convertToDto(Fruit fruit) { + return new FruitDto(fruit.getName(), fruit.getDescription()); } - private Fruit covertFromDto(FruitDto fruitDto) { - return new Fruit(fruitDto.getName(), fruitDto.getDescription(), STORE_NAME); + private Fruit convertFromDto(FruitDto fruitDto) { + return new Fruit(fruitDto.getName(), fruitDto.getDescription()); } } ---- -The `list` and `add` operations are executed for the `storeId` "acme". This is the partition key of -our data model. We can easily retrieve all rows from cassandra using that partition key. -They will be sorted by the clustering column. `FruitResource` is using `FruitService` which -encapsulates the data access logic. +Notice how `FruitResource` is being injected a `FruitService` instance automatically. -When creating the REST API we should not share the same entity object between REST API and data -access layers. They should not be coupled to allow the API to evolve independently of the storage -layer. This is the reason why the API is using a `FruitDto` class. This class will be used by -Quarkus to convert JSON to java objects for client requests and java objects to JSON for the -responses. The translation is done by quarkus-resteasy extension. +It is generally not recommended using the same entity object between the REST API and the data +access layer. These layers should indeed be decoupled and use distinct APIs in order to allow each +API to evolve independently of the other. This is the reason why our REST API is using a different +object: the `FruitDto` class – the word DTO stands for "Data Transfer Object". This DTO object will +be automatically converted to and from JSON in HTTP messages: [source,java] ---- @@ -272,15 +236,39 @@ public class FruitDto { } ---- +The translation to and from JSON is done automatically by the Quarkus RestEasy extension, which is +included in this guide's pom.xml file. If you want to add it manually to your application, add the +below snippet to your application's ppm.xml file: + +[source,xml] +---- + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jsonb + +---- + IMPORTANT: DTO classes used by the JSON serialization layer are required to have a default no-arg constructor. -== Configuring the Cassandra database +The conversion from DTO to JSON is handled automatically for us, but we still must convert from +`Fruit` to `FruitDto` and vice versa. This must be done manually, which is why we have two +conversion methods declared in `FruitResource`: `convertToDto` and `convertFromDto`. + +TIP: In our example, `Fruit` and `FruitDto` are very similar, so you might wonder why not use +`Fruit` everywhere. In real life cases though, it's not uncommon to see DTOs and entities having +very different structures. + +== Connecting to the Cassandra Database === Connecting to Apache Cassandra or DataStax Enterprise (DSE) -The main properties to configure are: `contact-points`, to access the Cassandra database, -`local-datacenter`, which is required by the driver, and – optionally – the keyspace to bind to. +The main properties to configure are: `contact-points`, to access the Cassandra database; +`local-datacenter`, which is required by the driver; and – optionally – the keyspace to bind to. A sample configuration should look like this: @@ -301,7 +289,7 @@ quarkus.cassandra.local-datacenter=datacenter1 quarkus.cassandra.keyspace=k1 ---- -If your cluster requires plain text authentication, you can also provide two more settings: +If your cluster requires plain text authentication, you must also provide two more settings: `username` and `password`. [source,properties] @@ -310,11 +298,15 @@ quarkus.cassandra.auth.username=john quarkus.cassandra.auth.password=s3cr3t ---- -=== Connecting to a cloud DataStax Astra database +=== Connecting to a DataStax Astra Cloud Database + +When connecting to link:https://astra.datastax.com[DataStax Astra], instead of providing a contact +point and a datacenter, you should provide a so-called _secure connect bundle_, which should point +to a valid path to an Astra secure connect bundle file. You can download your secure connect bundle +from the Astra web console. -When connecting to Astra, instead of providing a contact point and a datacenter, you should provide -`secure-connect-bundle`, which should point to a valid path to an Astra secure connect bundle, as -well as `username` and`password`, since authentication is always required on Astra clusters. +You will also need to provide a username and password, since authentication is always required on +Astra clusters. A sample configuration for DataStax Astra should look like this: @@ -326,110 +318,126 @@ quarkus.cassandra.auth.password=s3cr3t quarkus.cassandra.keyspace=k1 ---- -=== Advanced driver configuration +=== Advanced Driver Configuration You can configure other Java driver settings using `application.conf` or `application.json` files. -They need to be located in the classpath of your application. -All settings will be passed automatically to the underlying driver configuration mechanism. -Settings defined in `application.properties` with the `quarkus.cassandra` prefix will have priority -over settings defined in `application.conf` or `application.json`. +They need to be located in the classpath of your application. All settings will be passed +automatically to the underlying driver configuration mechanism. Settings defined in +`application.properties` with the `quarkus.cassandra` prefix will have priority over settings +defined in `application.conf` or `application.json`. To see the full list of settings, please refer to the -link:https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/[driver settings reference]. +link:https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/[driver +settings reference]. -== Running a Cassandra Database +== Running a Local Cassandra Database -By default, `CassandraClient` is configured to access a local Cassandra database on port 9042 (the -default Cassandra port). +By default, the Cassandra client is configured to access a local Cassandra database on port 9042 +(the default Cassandra port). -IMPORTANT: Make sure that the setting `quarkus.cassandra.local-datacenter` -matches the datacenter of your Cassandra cluster. +IMPORTANT: Make sure that the setting `quarkus.cassandra.local-datacenter` matches the datacenter of +your Cassandra cluster. TIP: If you don't know the name of your local datacenter, this value can be found by running the following CQL query: `SELECT data_center FROM system.local`. If you want to use Docker to run a Cassandra database, you can use the following command to launch -one: +one in the background: -[source,bash] +[source,shell] ---- -docker run \ - --name local-cassandra-instance \ - -p 7000:7000 \ - -p 7001:7001 \ - -p 7199:7199 \ - -p 9042:9042 \ - -p 9160:9160 \ - -p 9404:9404 \ - -d \ - launcher.gcr.io/google/cassandra3 +docker run --name local-cassandra-instance -p 9042:9042 -d cassandra ---- -Note that only the 9042 port is required. All others all optional but provide enhanced features -like JMX monitoring of the Cassandra instance. - Next you need to create the keyspace and table that will be used by your application. If you are using Docker, run the following commands: -[source,bash] +[source,shell] ---- docker exec -it local-cassandra-instance cqlsh -e "CREATE KEYSPACE IF NOT EXISTS k1 WITH replication = {'class':'SimpleStrategy', 'replication_factor':1}" -docker exec -it local-cassandra-instance cqlsh -e "CREATE TABLE IF NOT EXISTS k1.fruit(store_id text, name text, description text, PRIMARY KEY((store_id), name))" +docker exec -it local-cassandra-instance cqlsh -e "CREATE TABLE IF NOT EXISTS k1.fruit(name text PRIMARY KEY, description text)" ---- -If you're running Cassandra locally you can execute the cqlsh commands directly: +You can also use the CQLSH utility to interactively interrogate your database: -[source,bash] +[source,shell] ---- -cqlsh -e "CREATE KEYSPACE IF NOT EXISTS k1 WITH replication = {'class':'SimpleStrategy', 'replication_factor':1} -cqlsh -e "CREATE TABLE IF NOT EXISTS k1.fruit(store_id text, name text, description text, PRIMARY KEY((store_id), name)) +docker exec -it local-cassandra-instance cqlsh ---- -== Creating a frontend +== Testing the REST API + +In the project root directory: + +- Run `mvn clean package` and then `java -jar ./target/cassandra-quarkus-quickstart-*-runner.jar` to start the application; +- Or better yet, run the application in dev mode: `mvn clean quarkus:dev`. + +Now you can use curl commands to interact with the underlying REST API. + +To create a fruit: + +[source,shell] +---- +curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"name":"apple","description":"red and tasty"}' \ + http://localhost:8080/fruits +---- + +To retrieve fruits: + +[source,shell] +---- +curl -X GET http://localhost:8080/fruits +---- + +== Creating a Frontend Now let's add a simple web page to interact with our `FruitResource`. -Quarkus automatically serves static resources located under the `META-INF/resources` directory. -In the `src/main/resources/META-INF/resources` directory, add a `fruits.html` file with the content -from this link:https://github.com/datastax/cassandra-quarkus/tree/master/quickstart/src/main/resources/META-INF/resources/fruits.html[fruits.html] file in it. +Quarkus automatically serves static resources located under the `META-INF/resources` directory. In +the `src/main/resources/META-INF/resources` directory, add a `fruits.html` file with the contents +from link:src/main/resources/META-INF/resources/fruits.html[this file] in it. You can now interact with your REST service: -* start Quarkus with `mvn clean quarkus:dev` -* open a browser to `http://localhost:8080/fruits.html` -* add new fruits to the list via the form +* If you haven't done yet, start your application with `mvn clean quarkus:dev`; +* Point your browser to `http://localhost:8080/fruits.html`; +* Add new fruits to the list via the form. [[reactive]] -== Reactive Cassandra Client +== Reactive Programming with the Cassandra Client -When using `QuarkusCqlSession` you have access to reactive variant of methods that integrate with -Quarkus and Mutiny. +The +link:https://javadoc.io/doc/com.datastax.oss.quarkus/cassandra-quarkus-client/latest/com/datastax/oss/quarkus/runtime/api/session/QuarkusCqlSession.html[`QuarkusCqlSession` +interface] gives you access to a series of reactive methods that integrate seamlessly with Quarkus +and its reactive framework, Mutiny. TIP: If you're not familiar with Mutiny, read the -link:getting-started-reactive[Getting Started with Reactive guide] first. +link:https://quarkus.io/guides/getting-started-reactive[Getting Started with Reactive guide] first. -Let's rewrite the previous example using reactive programming with Mutiny. +Let's rewrite our application using reactive programming with Mutiny. -Firstly, we need to implement the `@Dao` that works in a reactive way: +First, let's to declare another DAO interface that works in a reactive way: [source,java] ---- @Dao -public interface FruitDaoReactive { +public interface ReactiveFruitDao { @Update - Uni updateAsync(Fruit fruitDao); + Uni updateAsync(Fruit fruit); @Select - MutinyMappedReactiveResultSet findByIdAsync(String id); + MutinyMappedReactiveResultSet findAll(); } ---- -Please note the usage of `MutinyMappedReactiveResultSet` - it is a specialized `Mutiny` type -converted from the original `Publisher` returned by the driver, which also exposes a few extra -methods, e.g. to obtain the query execution info. If you don't need anything in that interface, -you can also simply declare your method to return `Multi`: `Multi findByIdAsync(String id)`, +Note the usage of `MutinyMappedReactiveResultSet` - it is a specialized `Mutiny` type converted from +the original `Publisher` returned by the driver, which also exposes a few extra methods, e.g. to +obtain the query execution info. If you don't need anything in that interface, you can also simply +declare your method to return `Multi`: `Multi findAll()`, Similarly, the method `updateAsync` returns a `Uni` - it is automatically converted from the original result set returned by the driver. @@ -442,162 +450,354 @@ publisher is expected to emit at most one row, then complete. This is suitable f (they return no rows), or for read queries guaranteed to return one row at most (count queries, for example). -Next, we need to adapt the `FruitMapper` to construct a `FruitDaoReactive` instance: +Next, we need to adapt the `FruitMapper` to construct a `ReactiveFruitDao` instance: -[source, java] +[source,java] ---- @Mapper public interface FruitMapper { // the existing method omitted @DaoFactory - FruitDaoReactive fruitDaoReactive(); + ReactiveFruitDao reactiveFruitDao(); } ---- -Now, we can create a `FruitReactiveService` that leverages the reactive `@Dao`: +Now, we can create a `ReactiveFruitService` that leverages our reactive DAO: -[source, java] +[source,java] ---- @ApplicationScoped -public class FruitReactiveService { - - private final FruitDaoReactive fruitDao; +public class ReactiveFruitService { - @Inject - public FruitReactiveService(FruitDaoReactive fruitDao) { - this.fruitDao = fruitDao; - } + @Inject ReactiveFruitDao fruitDao; public Uni add(Fruit fruit) { return fruitDao.update(fruit); } - public Multi get(String id) { - return fruitDao.findById(id); + public Multi getAll() { + return fruitDao.findAll(); } } ---- -NOTE: The `get()` method above returns `Multi`, and the `add()` method returns `Uni`; these types -are compatible with the Quarkus reactive REST API. - -To integrate the reactive logic with REST API, you need to have a dependency to -`quarkus-resteasy-mutiny`: - -[source, xml] ----- - - io.quarkus - quarkus-resteasy-mutiny - ----- - -It provides an integration layer between `Multi`, `Uni` and the REST API. - -Finally, we can create a `FruitReactiveResource`: +Finally, we can create a `ReactiveFruitResource`: -[source, java] +[source,java] ---- @Path("/reactive-fruits") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class FruitReactiveResource { - private static final String STORE_NAME = "acme"; - @Inject FruitReactiveService service; +public class ReactiveFruitResource { + + @Inject ReactiveFruitService service; @GET public Multi getAll() { - return service - .get(STORE_NAME) - .map(fruit -> new FruitDto(fruit.getName(), fruit.getDescription())); + return service.getAll().map(this::convertToDto); } @POST - public Multi add(FruitDto fruitDto) { - Fruit fruit = covertFromDto(fruitDto); - return service.add(fruit).then(ignored -> getAll()); + public Uni add(FruitDto fruitDto) { + return service.add(convertFromDto(fruitDto)); + } + + private FruitDto convertToDto(Fruit fruit) { + return new FruitDto(fruit.getName(), fruit.getDescription()); } - private Fruit covertFromDto(FruitDto fruitDto) { - return new Fruit(fruitDto.getName(), fruitDto.getDescription(), STORE_NAME); + private Fruit convertFromDto(FruitDto fruitDto) { + return new Fruit(fruitDto.getName(), fruitDto.getDescription()); } } ---- -NOTE: All methods exposed via REST interface are returning reactive types from the Mutiny API. +The above resource is exposing a new endpoint, `reactive-fruits`. Its capabilities are identical to +the ones that we created before with `FruitResource`, but everything is handled in a reactive +fashion, without any blocking operation. -== Creating a reactive frontend +NOTE: The `getAll()` method above returns `Multi`, and the `add()` method returns `Uni`. These types +are the same Mutiny types that we met before; they are automatically recognized by the Quarkus +reactive REST API, so we don't need to convert them into JSON ourselves. -Now let's add a simple web page to interact with our `FruitReactiveResource`. -In the `src/main/resources/META-INF/resources` directory, add a `reactive-fruits.html` file with -the content from this -link:https://github.com/datastax/cassandra-quarkus/tree/master/quickstart/src/main/resources/META-INF/resources/reactive-fruits.html[reactive-fruits.html] -file in it. +To effectively integrate the reactive logic with the REST API, your application needs to declare a +dependency to the Quarkus RestEasy Mutiny extension: + +[source,xml] +---- + + io.quarkus + quarkus-resteasy-mutiny + +---- + +This dependency is already included in this guide's pom.xml, but if you are starting a new project +from scratch, make sure to include it. + +== Testing the Reactive REST API + +Run the application in dev mode as explained above, then you can use curl commands to interact with +the underlying REST API. + +To create a fruit using the reactive REST endpoint: + +[source,shell] +---- +curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"name":"banana","description":"yellow and sweet"}' \ + http://localhost:8080/reactive-fruits +---- + +To retrieve fruits with the reactive REST endpoint: + +[source,shell] +---- +curl -X GET http://localhost:8080/reactive-fruits +---- + +== Creating a Reactive Frontend + +Now let's add a simple web page to interact with our `ReactiveFruitResource`. In the +`src/main/resources/META-INF/resources` directory, add a `reactive-fruits.html` file with the +contents from link:src/main/resources/META-INF/resources/reactive-fruits.html[this file] in it. You can now interact with your reactive REST service: -* start Quarkus with `mvn clean quarkus:dev` -* open a browser to `http://localhost:8080/reactive-fruits.html` -* add new fruits to the list via the form +* If you haven't done yet, start your application with `mvn clean quarkus:dev`; +* Point your browser to `http://localhost:8080/reactive-fruits.html`; +* Add new fruits to the list via the form. +== Health Checks -== Connection Health Check +If you are using the Quarkus SmallRye Health extension, then the Cassandra client will automatically +add a readiness health check to validate the connection to the Cassandra cluster. This extension is +already included in this guide's pom.xml, but if you need to include it manually in your +application, add the following: -If you are using the `quarkus-smallrye-health` extension, `cassandra-quarkus` will automatically -add a readiness health check to validate the connection to the cluster. +[source,xml] +---- + + io.quarkus + quarkus-smallrye-health + +---- -So when you access the `/q/health/ready` endpoint of your application you will have information about -the connection validation status. +When health checks are available, you can access the `/health/ready` endpoint of your application +and have information about the connection validation status. + +Running in dev mode with `mvn clean quarkus:dev`, if you point your browser to +http://localhost:8080/health/ready you should see an output similar to the following one: + +[source,text] +---- +{ + "status": "UP", + "checks": [ + { + "name": "DataStax Apache Cassandra Driver health check", + "status": "UP", + "data": { + "cqlVersion": "3.4.4", + "releaseVersion": "3.11.7", + "clusterName": "Test Cluster", + "datacenter": "datacenter1", + "numberOfNodes": 1 + } + } + ] +} +---- -TIP: This behavior can be disabled by setting the `quarkus.cassandra.health.enabled` property to -`false` in your `application.properties`. +TIP: If you need health checks globally enabled in your application, but don't want to activate +Cassandra health checks, you can disable Cassandra health checks by setting the +`quarkus.cassandra.health.enabled` property to `false` in your `application.properties`. == Metrics -If you are using the `quarkus-smallrye-metrics` extension, `cassandra-quarkus` can provide metrics -about QuarkusCqlSession and Cassandra nodes. +The Cassandra Quarkus client can provide metrics about the Cassandra session and about individual +Cassandra nodes. It supports both Micrometer and MicroProfile. + +The first step to enable metrics is to add a few additional dependencies depending on the metrics +framework you plan to use. + +=== Enabling Metrics with Micrometer + +Micrometer is the recommended metrics framework in Quarkus applications since Quarkus 1.9. + +To enable Micrometer metrics in your application, you need to add the following to your pom.xml. + +For Quarkus 1.11+: + +[source,xml] +---- + + com.datastax.oss + java-driver-metrics-micrometer + + + io.quarkus + quarkus-micrometer-registry-prometheus + +---- + +For Quarkus < 1.11: + +[source,xml] +---- + + com.datastax.oss + java-driver-metrics-micrometer + + + io.quarkus + quarkus-micrometer + + + io.micrometer + micrometer-registry-prometheus + +---- + +This guide uses Micrometer, so the above dependencies are already included in this guide's pom.xml. -TIP: This behavior must first be enabled by setting the `quarkus.cassandra.metrics.enabled` -property to `true` in your `application.properties`. +=== Enabling Metrics with MicroProfile Metrics -The next step that you need to do is set explicitly which metrics should be enabled. +Remove any dependency to Micrometer from your pom.xml, then add the following ones instead: -The `quarkus.cassandra.metrics.session-enabled` and `quarkus.cassandra.metrics.node-enabled` -properties should be used for enabling metrics; the former should contain a list of session-level -metrics to enable, while the latter should contain a list of node-level metrics to enable. Both -properties accept a comma-separated list of valid metric names. +[source,xml] +---- + + com.datastax.oss + java-driver-metrics-microprofile + + + io.quarkus + quarkus-smallrye-metrics + +---- + +=== Enabling Cassandra Metrics -For example, to enable `session.connected-nodes`, `session.bytes-sent`, and -`node.pool.open-connections` you should add the following settings to your `application.properties`: +Even when metrics are enabled in your application, the Cassandra client will not report any metrics, +unless you opt-in for this feature. So your next step is to enable Cassandra metrics in your +`application.properties` file. -[source, properties] +[source,properties] ---- quarkus.cassandra.metrics.enabled=true -quarkus.cassandra.metrics.session-enabled=connected-nodes,bytes-sent -quarkus.cassandra.metrics.node-enabled=pool.open-connections ---- -For the full list of available metrics, please refer to the -link:https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/[driver settings reference] -and the `advanced.metrics` section. +That's it! + +The final (and optional) step is to customize which specific Cassandra metrics you would like the +Cassandra client to track. Several metrics can be tracked; if you skip this step, a default set of +useful metrics will be automatically tracked. + +TIP: For the full list of available metric names, please refer to the +link:https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/[driver +settings reference] page; search for the `advanced.metrics` section. +Also, Cassandra driver metrics are covered in detail in the +https://docs.datastax.com/en/developer/java-driver/latest/manual/core/metrics/[driver manual]. -When metrics are enabled and you access the `/q/metrics` endpoint of your application, -you will see metric reports for all enabled metrics. +If you do wish to customize which metrics to track, you should use the following properties: -== Building a native executable +* `quarkus.cassandra.metrics.session.enabled` should contain the session-level metrics to enable +(metrics that are global to the session). +* `quarkus.cassandra.metrics.node.enabled` should contain the node-level metrics to enable (metrics +for which each node contacted by the Cassandra client gets its own metric value). -You can use the Cassandra client in a native executable. +Both properties accept a comma-separated list of valid metric names. -You can build a native executable with the `mvn clean package -Dnative` command. +For example, let's assume that you wish to enable the following three Cassandra metrics: -Running it is as simple as executing `./target/quickstart-1.0.0-SNAPSHOT-runner`. +* Session-level: `session.connected-nodes` and `session.bytes-sent`; +* Node-level: `node.pool.open-connections`. + +Then you should add the following settings to your `application.properties`: + +[source,properties] +---- +quarkus.cassandra.metrics.enabled=true +quarkus.cassandra.metrics.session.enabled=connected-nodes,bytes-sent +quarkus.cassandra.metrics.node.enabled=pool.open-connections +---- + +This guide's `application.properties` file has already many metrics enabled; you can use its metrics +list as a good starting point for exposing useful Cassandra metrics in your application. + +When metrics are properly enabled, metric reports for all enabled metrics are available at the +`/metrics` REST endpoint of your application. + +Running in dev mode with `mvn clean quarkus:dev`, if you point your browser to +`http://localhost:8080/metrics` you should see a list of metrics; search for metrics whose names +contain `cassandra`. + +IMPORTANT: For Cassandra metrics to show up, the Cassandra client needs to be initialized and +connected; if you are using lazy initialization (see below), you won't see any Cassandra metrics +until your application actually connects and hits the database for the first time. + +== Running in native mode + +If you installed GraalVM, you can link:https://quarkus.io/guides/building-native-image[build a +native image] using: + +[source,shell] +---- +mvn clean package -Dnative +---- + +Beware that native compilation can take a significant amount of time! Once the compilation is done, +you can run the native executable as follows: + +[source,shell] +---- +./target/cassandra-quarkus-quickstart-*-runner +---- You can then point your browser to `http://localhost:8080/fruits.html` and use your application. +== Eager vs Lazy Initialization + +This extension allows you to inject either: + +- a `QuarkusCqlSession` bean; +- or the asynchronous version of this bean, that is, `CompletionStage`; +- or the reactive version of this bean, that is, `Uni`. + +The most straightforward approach is obviously to inject `QuarkusCqlSession` directly. This should +work just fine for most applications; however, the `QuarkusCqlSession` bean needs to be initialized +before it can be used, and this process is blocking. + +Fortunately, it is possible to control when the initialization should happen: the +`quarkus.cassandra.init.eager-init` parameter determines if the `QuarkusCqlSession` bean should be +initialized on its first access (lazy) or when the application is starting (eager). The default +value of this parameter is `false`, meaning the init process is lazy: the `QuarkusCqlSession` bean +will be initialized lazily on its first access – for example, when there is a first REST request +that needs to interact with the Cassandra database. + +Using lazy initialization speeds up your application startup time, and avoids startup failures if +the Cassandra database is not available. However, it could also prove dangerous if your code is +fully asynchronous, e.g. if you are using https://quarkus.io/guides/reactive-routes[reactive +routes]: indeed, the lazy initialization could accidentally happen on a thread that is not allowed +to block, such as a Vert.x event loop thread. Therefore, setting `quarkus.cassandra.init.eager-init` +to `false` and injecting `QuarkusCqlSession` should be avoided in these contexts. + +If you want to use Vert.x (or any other reactive framework) and keep the lazy initialization +behavior, you should instead inject only `CompletionStage` or +`Uni`. When injecting these beans, the initialization process will be triggered +lazily, but it will happen in the background, in a non-blocking way, leveraging the Vert.x event +loop. This way you don't risk blocking the Vert.x thread. + +Alternatively, you can set `quarkus.cassandra.init.eager-init` to true: in this case the session +bean will be initialized eagerly during application startup, on the Quarkus main thread. This would +eliminate any risk of blocking a Vert.x thread, at the cost of making your startup time (much) +longer. + == Conclusion Accessing a Cassandra database from a client application is easy with Quarkus and the Cassandra -extension, which provides configuration and native support for the DataStax Java driver for -Apache Cassandra. +extension, which provides configuration and native support for the DataStax Java driver for Apache +Cassandra. diff --git a/docs/src/main/asciidoc/cdi-integration.adoc b/docs/src/main/asciidoc/cdi-integration.adoc index 93e2440aa1a9f7..13a23792f5ce61 100644 --- a/docs/src/main/asciidoc/cdi-integration.adoc +++ b/docs/src/main/asciidoc/cdi-integration.adoc @@ -340,8 +340,8 @@ IMPORTANT: A build step that consumes the `ObserverRegistrationPhaseBuildItem` s @BuildStep void syntheticObserver(ObserverRegistrationPhaseBuildItem observerRegistrationPhase, BuildProducer myBuildItem, - BuildProducer observerConfigurators) { - observerConfigurators.produce(new ObserverConfiguratorBuildItem(observerRegistrationPhase.getContext() + BuildProducer observerConfigurationRegistry) { + observerConfigurationRegistry.produce(new ObserverConfiguratorBuildItem(observerRegistrationPhase.getContext() .configure() .beanClass(DotName.createSimple(MyBuildStep.class.getName())) .observedType(String.class) diff --git a/docs/src/main/asciidoc/conditional-extension-dependencies.adoc b/docs/src/main/asciidoc/conditional-extension-dependencies.adoc new file mode 100644 index 00000000000000..3e5cc5927a9024 --- /dev/null +++ b/docs/src/main/asciidoc/conditional-extension-dependencies.adoc @@ -0,0 +1,178 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc +//// += Quarkus - Conditional Extension Dependencies + +include::./attributes.adoc[] + +Quarkus extension dependencies are usually configured in the same way as any other project dependencies in the project's build file, e.g. the Maven `pom.xml` or the Gradle build scripts. However, there are dependency types that aren't yet supported out-of-the-box by Maven and Gradle. What we refer here to as "conditional dependencies" is one example. + +== Conditional Dependencies + +The idea behind the notion of the conditional dependency is that such a dependency must be activated only if a certain condition is satisfied. If the condition is not satisfied then the dependency **must not** be activated. In that regard, conditional dependencies can be categorized as optional, i.e. they may or may not appear in the resulting set of project dependencies. + +In which cases could conditional dependencies be useful? A typical example would be a component that should be activated **only** in case all of its required dependencies are available. If one or more of the component's required dependencies aren't available, instead of failing, the component should simply not be activated. + +== Quarkus Conditional Extension Dependencies + +Quarkus supports conditional extension dependencies. I.e. one Quarkus extension may declare one or more conditional dependencies on other Quarkus extensions. Conditional dependencies on and from non-extension artifacts aren't supported. + +Let's take the following scenario as an example: `quarkus-extension-a` has an optional dependency on `quarkus-extension-b` which should be included in a Quarkus application only if `quarkus-extension-c` is found among its dependencies (direct or transitive). In other words, the presence of `quarkus-extension-c` is the condition which, if satisfied, enables `quarkus-extension-b` during the build of a Quarkus application. + +The condition which triggers activation of an extension is configured in the extension's descriptor, which is included into the runtime artifact of the extension as `META-INF/quarkus-extension.properties`. Given that extension descriptor is generated by the Quarkus plugin at extension build time, extension developers can add the following configuration to express the condition which would have to be satisfied for the extension to be activated: + +[source,xml] +---- + + + + + quarkus-extension-b <1> + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + process-resources + + extension-descriptor <2> + + + <3> + org.acme:quarkus-extension-c <4> + + + + + + + +---- + +<1> runtime Quarkus extension artifact ID, in our example `quarkus-extension-b`; +<2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with; +<3> configuration of the condition which will have to be satisfied for this extension to be included into a Quarkus application expressed as a list of artifacts that must be present among the application dependencies; +<4> an artifact key (in the format of `groupId:artifactId[::]` but typically simply `:`) of the artifact that must be present among the application dependencies for the condition to be satisfied. + +NOTE: In the example above the `artifact` used in the condition configuration happens to be a runtime Quarkus extension artifact but it could as well be any other artifact. There could also be more than one `artifact` element in the body of `dependencyCondition`. + +Now, having a dependency activating condition in the descriptor of `quarkus-extension-b`, other extensions may declare a conditional dependency on it. + +A conditional dependency is configured in the runtime artifact of a Quarkus extension. In our example, it's the `quarkus-extension-a` that has a conditional dependency on `quarkus-extension-b`, which can be expressed in two ways. + +=== Declaring a dependency as `optional` + +If an extension was configured with a dependency condition in its descriptor, other extensions may configure a conditional dependency on it by simply adding `true` to the dependency configuration. In our example it would look like this: + +[source,xml] +---- + + + + + quarkus-extension-a <1> + + + + + + org.acme + quarkus-extension-b <2> + true + + + +---- + +<1> the runtime extension artifact `quarkus-extension-a` +<2> declares an optional Maven dependency on the runtime extension artifact `quarkus-extension-b` + +IMPORTANT: In general, for every runtime extension artifact dependency on another runtime extension artifact there must be a corresponding deployment extension artifact dependency on the other deployment extension artifact. And if the runtime dependency is declared as optional then the corresponding deployment dependency **must** also be configured as optional. + +[source,xml] +---- + + + + + quarkus-extension-a-deployment <1> + + + + + + org.acme + quarkus-extension-b-deployment <2> + true + + + +---- + +<1> the deployment extension artifact `quarkus-extension-a-deployment` +<2> declares an optional Maven dependency on the deployment extension artifact `quarkus-extension-b-deployment` + +Normally, optional Maven extension dependencies are ignored by the Quarkus dependency resolver at build time. In this case though, the optional dependency `quarkus-extension-b` includes a dependency condition in its extension descriptor, which turns this optional Maven dependency into a Quarkus conditional extension dependency. + +IMPORTANT: If `quarkus-extension-b` wasn't declared as `true` that would make `quarkus-extension-b` a required dependency of `quarkus-extension-a` and its dependency condition would be ignored. + +=== Declaring a conditional dependency in the Quarkus extension descriptor + +Conditional dependencies can also be configured in the Quarkus extension descriptor. The conditional dependency configured above could be expressed in the extension descriptor of `quarkus-extension-a` as: + +[source,xml] +---- + + + + + quarkus-extension-a <1> + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + process-resources + + extension-descriptor <2> + + + <3> + org.acme:quarkus-extension-b:${b.version} <4> + + + + + + + +---- + +<1> runtime Quarkus extension artifact ID, in our example `quarkus-extension-a` +<2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with +<3> conditional dependency configuration element +<4> artifact coordinates of conditional dependencies on other extensions. + +In this case, the Maven dependency is not at all required in the `pom.xml`. diff --git a/docs/src/main/asciidoc/config-reference.adoc b/docs/src/main/asciidoc/config-reference.adoc index 0e3aaaae3e37e8..4ee6f4d16dbff5 100644 --- a/docs/src/main/asciidoc/config-reference.adoc +++ b/docs/src/main/asciidoc/config-reference.adoc @@ -13,10 +13,10 @@ include::./attributes.adoc[] :toc: In this reference guide we're going to describe various aspects of Quarkus configuration. -A Quarkus application and Quarkus itself (core and extensions) are both configured via the same mechanism that leverages the https://microprofile.io/project/eclipse/microprofile-config[MicroProfile Config] API. -Quarkus configuration is based on https://github.com/smallrye/smallrye-config[SmallRye Config], an implementation of the MicroProfile Config specification. -All of the standard features are supported. -Moreover, there are several additional features which are made available by the SmallRye Config project as well as by Quarkus itself. +A Quarkus application and Quarkus itself (core and extensions) are both configured via the same mechanism that leverages +the https://github.com/smallrye/smallrye-config[SmallRye Config] API an implementation of the +https://microprofile.io/project/eclipse/microprofile-config[MicroProfile Config] specification. Moreover, there are +several additional features which are made available by Quarkus itself. TIP: If you're looking for information how to make a Quarkus extension configurable then see the <> guide. @@ -88,7 +88,7 @@ TIP: Quarkus supports the use of <>. For the purpose of this guide, we will use an application configuration file located in `src/main/resources/application.properties`. Edit the file with the following content: diff --git a/docs/src/main/asciidoc/context-propagation.adoc b/docs/src/main/asciidoc/context-propagation.adoc index 147ca885f85450..5d6f9698ea88cc 100644 --- a/docs/src/main/asciidoc/context-propagation.adoc +++ b/docs/src/main/asciidoc/context-propagation.adoc @@ -18,6 +18,7 @@ If you write reactive/async code, you have to cut your work into a pipeline of c as well as `ThreadLocal` variables stop working, because your reactive code gets executed in another thread, after the caller ran its `finally` block. +link:https://github.com/smallrye/smallrye-context-propagation[SmallRye Context Propagation] an implementation of link:https://github.com/eclipse/microprofile-context-propagation[MicroProfile Context Propagation] was made to make those Quarkus extensions work properly in reactive/async settings. It works by capturing those contextual values that used to be in thread-locals, and restoring them when your code is called. diff --git a/docs/src/main/asciidoc/datasource.adoc b/docs/src/main/asciidoc/datasource.adoc index f011b9794b121b..0074005050b66b 100644 --- a/docs/src/main/asciidoc/datasource.adoc +++ b/docs/src/main/asciidoc/datasource.adoc @@ -40,6 +40,7 @@ please refer to the link:reactive-sql-clients[Reactive SQL clients guide]. This is a quick introduction to datasource configuration. If you want a better understanding of how all this works, this guide has a lot more information in the subsequent paragraphs. +[[dev-services]] === Zero Config Setup (DevServices) When testing or running in dev mode Quarkus can even provide you with a zero config database out of the box, a feature diff --git a/docs/src/main/asciidoc/dev-mode-differences.adoc b/docs/src/main/asciidoc/dev-mode-differences.adoc new file mode 100644 index 00000000000000..4a00bd60e5b23f --- /dev/null +++ b/docs/src/main/asciidoc/dev-mode-differences.adoc @@ -0,0 +1,106 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Quarkus - How dev-mode differs from a production application + +include::./attributes.adoc[] + +This document explains how the dev-mode in Quarkus differs from a production application. + +== Intro + +Quarkus provides a dev-mode (explained in more detail link:maven-tooling.adoc#dev-mode[here] and link:gradle-tooling.adoc#dev-mode[here]) which greatly aids +during development but should *NEVER* be used in production. + +[[architectural-differences]] +== Architectural differences + +Feature sets aside, the Quarkus application that is run under dev-mode differs architecturally from the production application (i.e. the one that is run using `java -jar ...`). + +In dev-mode, Quarkus uses a ClassLoader hierarchy (explained in detail link:class-loading-reference.adoc[here]) that enables the live reload of user code +without requiring a rebuild and restart of the application. + +In a production application, the aforementioned class loading infrastructure is entirely absent - there is a single, purpose built ClassLoader that loads (almost) all classes and dependencies. + +== Dev-mode features + +In keeping with the mantra of providing developer joy, Quarkus provides a host of features when dev-mode is enabled. The most important features are: + +=== Live reload + +This mightily important feature needs no introduction and has already been mentioned in the <> section. + +=== Dev UI + +Quarkus provides a very useful link:dev-ui.adoc[UI] accessible from the browser at `/q/dev`. This UI allows a developer to see the state of the application, but +also provides access to various actions that can change that state (depending on the extensions that are present). +Examples of such operations are: + +* Changing configuration values +* Running Database migration scripts +* Clearing of caches +* Running scheduled operations +* Building a container + +=== Error pages + +In an effort to make development errors very easy to diagnose, Quarkus provides various detailed error pages when running in dev-mode. + +=== Database import scripts + +The `quarkus-hibernate-orm` extension will run the `import.sql` script in `src/main/resources` when Quarkus is running in dev-mode. More details can be found link:hibernate-orm.adoc#dev-mode[here]. + +=== Dev Services + +When testing or running in dev-mode Quarkus can even provide you with a zero config database out of the box, a feature we refer to as DevServices. +More information can be found link:datasource.adoc#dev-services[here]. + +=== Swagger UI + +The `quarkus-smallrye-openapi` extension will expose the Swagger UI when Quarkus is running in dev-mode. Additional information can be found link:openapi-swaggerui.adoc#dev-mode[here]. + +=== GraphQL UI + +The `quarkus-smallrye-graphql` extension will expose the GraphiQL UI when Quarkus is running in dev-mode. More details can be found link:smallrye-graphql.adoc#ui[here]. + +=== Health UI + +The `quarkus-smallrye-health` extension will expose the Health UI when Quarkus is running in dev-mode. link:smallrye-health.adoc#ui[This] section provides additional information. + +=== Mock mailer + +The `quarkus-mailer` extension will enable an in-memory mock mail server when Quarkus is running in dev-mode. See link:mailer.adoc#testing[this] for more details. + + +=== gRPC + +* The gRPC Reflection Service is enabled in dev mode by default. That lets you use tools such as `grpcurl`. In production mode, the reflection service is disabled. You can enable it explicitly using `quarkus.grpc-server.enable-reflection-service=true`. + +* In dev-mode, `quarkus.grpc.server.instances` has no effect. + +=== Others + +There might be other configuration properties (depending on the extensions added to the application) that have no effect in dev-mode. + + +== Performance implications + +In dev-mode minimizing the runtime footprint of the application is not the primary objective (although Quarkus still starts plenty fast and consumes little memory even in dev-mode) - the primary objective +is enabling developer joy. +Therefore, many more classes are loaded and build time operations also take place every time a live-reload is performed. + +In contrast, in a production application the main objective for Quarkus is to consume the least amount of memory and startup in the smallest amount of time. +Thus, when running the production application, build time operations are not performed (by definition) and various infrastructure classes needed at build time are not present at all at runtime. +Furthermore, the purpose built ClassLoader that comes with the link:maven-tooling.adoc#fast-jar[fast-jar] package type ensures that class lookup is done as fast as possible while also keeping +the minimum amount of jars in memory. + +== Security implications + +Perhaps the most important reason why dev-mode applications should not be run in production is that the dev-mode allows reading information that could be confidential (via the Dev-UI) +while also giving access to operations that could be destructive (either by exposing endpoints that should not be available in production application or via the Dev-UI). + +== Native binary + +When a native binary is created (explained in detail link:building-native-image[here]), it is *always* built from a production application. diff --git a/docs/src/main/asciidoc/elasticsearch.adoc b/docs/src/main/asciidoc/elasticsearch.adoc index dbc4ae998597ef..3c019f36638a46 100644 --- a/docs/src/main/asciidoc/elasticsearch.adoc +++ b/docs/src/main/asciidoc/elasticsearch.adoc @@ -282,7 +282,6 @@ import java.nio.file.Paths; import java.security.KeyStore; @ElasticsearchClientConfig -@Dependent public class SSLContextConfigurator implements RestClientBuilder.HttpClientConfigCallback { @Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { @@ -307,6 +306,12 @@ public class SSLContextConfigurator implements RestClientBuilder.HttpClientConfi ---- See https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/_encrypted_communication.html[Elasticsearch documentation] for more details on this particular example. +[NOTE] +==== +Classes marked with `@ElasticsearchClientConfig` are made application scoped CDI beans by default. +You can override the scope at the class level if you prefer a different scope. +==== + == Running an Elasticsearch cluster As by default, the Elasticsearch client is configured to access a local Elasticsearch cluster on port 9200 (the default Elasticsearch port), diff --git a/docs/src/main/asciidoc/getting-started-reactive.adoc b/docs/src/main/asciidoc/getting-started-reactive.adoc index 0976da65afef5c..a6d8ec483513c9 100644 --- a/docs/src/main/asciidoc/getting-started-reactive.adoc +++ b/docs/src/main/asciidoc/getting-started-reactive.adoc @@ -163,7 +163,7 @@ Uni uni = }); ---- -Last but not least, Mutiny has built-in integration with MicroProfile Context Propagation so you can propagate transactions, traceability data, and so on in your reactive pipeline. +Last but not least, Mutiny has built-in integration with SmallRye Context Propagation so you can propagate transactions, traceability data, and so on in your reactive pipeline. But enough talking, let's get our hands dirty! @@ -608,16 +608,6 @@ public class FruitResource { this.client = client; } - private void initdb() { - client.query("DROP TABLE IF EXISTS fruits").execute() - .flatMap(r -> client.query("CREATE TABLE fruits (id SERIAL PRIMARY KEY, name TEXT NOT NULL)").execute()) - .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Kiwi')").execute()) - .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Durian')").execute()) - .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Pomelo')").execute()) - .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Lychee')").execute()) - .await().indefinitely(); - } - @GET public Multi get() { return Fruit.findAll(client); @@ -755,13 +745,9 @@ import javax.ws.rs.core.MediaType; @Path("/fruit-data") public class ResourceUsingWebClient { - @Inject - Vertx vertx; - - private WebClient client; + private final WebClient client; - @PostConstruct - void initialize() { + public ResourceUsingWebClient(Vertx vertx) { this.client = WebClient.create(vertx, new WebClientOptions().setDefaultHost("fruityvice.com").setDefaultPort(443).setSsl(true) .setTrustAll(true)); diff --git a/docs/src/main/asciidoc/gradle-tooling.adoc b/docs/src/main/asciidoc/gradle-tooling.adoc index eb2f0361e6aad6..426860ee22c587 100644 --- a/docs/src/main/asciidoc/gradle-tooling.adoc +++ b/docs/src/main/asciidoc/gradle-tooling.adoc @@ -160,6 +160,7 @@ You can install all extensions which match a globbing pattern: ./gradlew addExtension --extensions="hibernate*" ---- +[[dev-mode]] == Development mode Quarkus comes with a built-in development mode. diff --git a/docs/src/main/asciidoc/grpc-getting-started.adoc b/docs/src/main/asciidoc/grpc-getting-started.adoc index e19de7e4cea905..8a082ab412500a 100644 --- a/docs/src/main/asciidoc/grpc-getting-started.adoc +++ b/docs/src/main/asciidoc/grpc-getting-started.adoc @@ -224,7 +224,8 @@ annotated method on a worker thread instead of the I/O thread (event-loop). The services are _served_ by a _server_. Available services (_CDI beans_) are automatically registered and exposed. -By default, the server is exposed on `localhost:9000`, and uses _plain-text_ (so no TLS). +By default, the server is exposed on `localhost:9000`, and uses _plain-text_ (so no TLS) when +running normally, and `localhost:9001` for tests. Run the application using: `mvn quarkus:dev`. diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index efa82492f36e77..24fc0747481c98 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -414,6 +414,7 @@ or jars, you can make sure that your jar is treated like a Quarkus application l This will allow Quarkus to index and enhance your entities as if they were inside the current project. +[[dev-mode]]] == Hibernate ORM in development mode Quarkus development mode is really useful for applications that mix front end or services and database access. diff --git a/docs/src/main/asciidoc/http-reference.adoc b/docs/src/main/asciidoc/http-reference.adoc index 5ff491113760d8..0c3f6807f3e7da 100644 --- a/docs/src/main/asciidoc/http-reference.adoc +++ b/docs/src/main/asciidoc/http-reference.adoc @@ -125,7 +125,7 @@ The password can be set in your `application.properties` (in plain-text) using t quarkus.http.ssl.certificate.key-store-password=your-password ---- -However, instead of providing the password as plain-text in the configuration file (which is considered bad practice), it can instead be supplied (using link:https://microprofile.io/project/eclipse/microprofile-config[MicroProfile config]) +However, instead of providing the password as plain-text in the configuration file (which is considered bad practice), it can instead be supplied (using link:https://github.com/eclipse/microprofile-config[MicroProfile Config]) as the environment variable `QUARKUS_HTTP_SSL_CERTIFICATE_KEY_STORE_PASSWORD`. This will also work in tandem with link:https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables[Kubernetes secrets]. diff --git a/docs/src/main/asciidoc/kafka-streams.adoc b/docs/src/main/asciidoc/kafka-streams.adoc index 474774966661a1..fcc087a27d2a1b 100644 --- a/docs/src/main/asciidoc/kafka-streams.adoc +++ b/docs/src/main/asciidoc/kafka-streams.adoc @@ -1252,7 +1252,7 @@ content-length: 265 This guide has shown how you can build stream processing applications using Quarkus and the Kafka Streams APIs, both in JVM and native modes. For running your KStreams application in production, you could also add health checks and metrics for the data pipeline. -Refer to the Quarkus guides on link:micrometer[Micrometer], link:microprofile-metrics[MicroProfile Metrics], and link:microprofile-health[health checks] to learn more. +Refer to the Quarkus guides on link:micrometer[Micrometer], link:smallrye-metrics[SmallRye Metrics], and link:smallrye-health[SmallRye Health] to learn more. == Configuration Reference diff --git a/docs/src/main/asciidoc/kafka.adoc b/docs/src/main/asciidoc/kafka.adoc index 553910a150c300..7a774e6e6c5e19 100644 --- a/docs/src/main/asciidoc/kafka.adoc +++ b/docs/src/main/asciidoc/kafka.adoc @@ -7,7 +7,7 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize MicroProfile Reactive Messaging to interact with Apache Kafka. +This guide demonstrates how your Quarkus application can utilize SmallRye Reactive Messaging to interact with Apache Kafka. == Prerequisites @@ -1135,6 +1135,6 @@ Type: _boolean_ | false | `true` == Going further This guide has shown how you can interact with Kafka using Quarkus. -It utilizes MicroProfile Reactive Messaging to build data streaming applications. +It utilizes SmallRye Reactive Messaging to build data streaming applications. If you want to go further check the documentation of https://smallrye.io/smallrye-reactive-messaging[SmallRye Reactive Messaging], the implementation used in Quarkus. diff --git a/docs/src/main/asciidoc/kubernetes-client.adoc b/docs/src/main/asciidoc/kubernetes-client.adoc index 5f0b86e57c9787..a14257d04fafea 100644 --- a/docs/src/main/asciidoc/kubernetes-client.adoc +++ b/docs/src/main/asciidoc/kubernetes-client.adoc @@ -113,11 +113,9 @@ public class KubernetesClientTest { final Pod pod1 = new PodBuilder().withNewMetadata().withName("pod1").withNamespace("test").and().build(); final Pod pod2 = new PodBuilder().withNewMetadata().withName("pod2").withNamespace("test").and().build(); - mockServer.expect().get().withPath("/api/v1/namespaces/test/pods") - .andReturn(200, - new PodListBuilder().withNewMetadata().withResourceVersion("1").endMetadata().withItems(pod1, pod2) - .build()) - .always(); + // Set up Kubernetes so that our "pretend" pods are created + mockServer.getClient().pods().create(pod1); + mockServer.getClient().pods().create(pod2); } @Test @@ -140,6 +138,42 @@ Note that to take advantage of these features, the `quarkus-test-kubernetes-clie ---- +By default, the mock server will be in CRUD mode, so you have to use the client to build your state before your application can retrieve it, +but you can also set it up in non-CRUD mode and mock all HTTP requests made to Kubernetes: + +[source%nowrap,java] +---- +// you can even configure aspects like crud, https and port on this annotation +@WithKubernetesTestServer(crud = false) +@QuarkusTest +public class KubernetesClientTest { + + @KubernetesTestServer + KubernetesServer mockServer; + + @BeforeEach + public void before() { + final Pod pod1 = new PodBuilder().withNewMetadata().withName("pod1").withNamespace("test").and().build(); + final Pod pod2 = new PodBuilder().withNewMetadata().withName("pod2").withNamespace("test").and().build(); + + // Mock any HTTP request to Kubernetes pods so that our pods are returned + mockServer.expect().get().withPath("/api/v1/namespaces/test/pods") + .andReturn(200, + new PodListBuilder().withNewMetadata().withResourceVersion("1").endMetadata().withItems(pod1, pod2) + .build()) + .always(); + } + + @Test + public void testInteractionWithAPIServer() { + RestAssured.when().get("/pod/test").then() + .body("size()", is(2)); + } + +} +---- + + Alternately, you can create a `CustomKubernetesMockServerTestResource.java` to ensure all your `@QuarkusTest` enabled test classes share the same mock server setup: [source%nowrap,java] diff --git a/docs/src/main/asciidoc/mailer.adoc b/docs/src/main/asciidoc/mailer.adoc index 1c692d72cf0938..a03045f5619178 100644 --- a/docs/src/main/asciidoc/mailer.adoc +++ b/docs/src/main/asciidoc/mailer.adoc @@ -240,7 +240,7 @@ It's also possible to inject a mail template, where the message body is created public class MailingResource { @CheckedTemplate - class Templates { + static class Templates { public static native MailTemplateInstance hello(String name); <1> } @@ -312,6 +312,7 @@ public Uni send() { <1> <1> The method returns a `Uni` <2> Transform the `Uni` returned by `MailTemplate` into `Unit` +[[testing]]] == Testing email sending Because it is very inconvenient to send emails during development and testing, you can set the `quarkus.mailer.mock` boolean diff --git a/docs/src/main/asciidoc/maven-tooling.adoc b/docs/src/main/asciidoc/maven-tooling.adoc index 0cfb5cf4abf0fc..bf0025aae2b394 100644 --- a/docs/src/main/asciidoc/maven-tooling.adoc +++ b/docs/src/main/asciidoc/maven-tooling.adoc @@ -125,6 +125,7 @@ You can install all extensions which match a globbing pattern : ./mvnw quarkus:add-extension -Dextensions="hibernate-*" ---- +[[dev-mode]] == Development mode Quarkus comes with a built-in development mode. @@ -515,6 +516,7 @@ If you have not used <>, add the following <5> Enable the `native` package type. The build will therefore produce a native executable. <6> If you want to test your native executable with Integration Tests, add the following plugin configuration. Test names `*IT` and annotated `@NativeImageTest` will be run against the native executable. See the link:building-native-image[Native executable guide] for more info. +[[fast-jar]] === Using fast-jar `fast-jar` is now the default quarkus package type. The result of `./mvnw package` command is a new directory under `target` named `quarkus-app`. diff --git a/docs/src/main/asciidoc/micrometer.adoc b/docs/src/main/asciidoc/micrometer.adoc index 860e7c8c755a40..5981f1410bd61f 100644 --- a/docs/src/main/asciidoc/micrometer.adoc +++ b/docs/src/main/asciidoc/micrometer.adoc @@ -7,8 +7,8 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize the Micrometer -metrics library for runtime and application metrics. +This guide demonstrates how your Quarkus application can utilize the Micrometer metrics library for runtime and +application metrics. Apart from application-specific metrics, which are described in this guide, you may also utilize built-in metrics exposed by various Quarkus extensions. These are described in the guide for each particular extension that supports @@ -27,24 +27,34 @@ To complete this guide, you need: == Architecture -In this example, we build a very simple microservice which offers one REST endpoint and that determines -if a number is prime. +Micrometer defines a core library providing a registration mechanism for Metrics, and core metric types (Counters, +Gauges, Timers, Distribution Summaries, etc.). These core types provide an abstraction layer that can be adapted to +different backend monitoring systems. In essence, your application (or a library) can `register` a `Counter`, +`Gauge`, `Timer`, or `DistributionSummary` with a `MeterRegistry`. Micrometer will then delegate that registration to +one or more implementations, where each implementation handles the unique considerations for the associated +monitoring stack. + +Micrometer uses naming conventions to translate between registered Meters and the conventions used by various backend +registries. Meter names, for example, should be created and named using dots to separate segments, `a.name.like.this`. +Micrometer then translates that name into the format that the selected registry prefers. Prometheus +uses underscores, which means the previous name will appear as `a_name_like_this` in Prometheus-formatted metrics +output. == Solution We recommend that you follow the instructions in the next sections and create the application step by step. -However, you can go right to the completed example. +You can skip right to the solution if you prefer. Either: -Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. +* Clone the git repository: `git clone {quickstarts-clone-url}`, or +* Download an {quickstarts-archive-url}[archive]. The solution is located in the `micrometer-quickstart` {quickstarts-tree-url}/micrometer-quickstart[directory]. == Creating the Maven Project -Micrometer defines a core library and a set of additional libraries that support different monitoring systems. -Quarkus Micrometer extensions are structured similarly: `quarkus-micrometer` provides core micrometer support and -runtime integration and other supporting Quarkus and Quarkiverse extensions bring in additional dependencies -and requirements to support specific monitoring systems. +Quarkus Micrometer extensions are structured similarly to Micrometer itself: `quarkus-micrometer` provides core +micrometer support and runtime integration and other Quarkus and Quarkiverse extensions bring in additional +dependencies and requirements to support specific monitoring systems. For this example, we'll use the Prometheus registry. @@ -55,7 +65,7 @@ First, we need a new project. Create a new project with the following command: mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=micrometer-quickstart \ - -DclassName="org.acme.micrometer.PrimeNumberResource" \ + -DclassName="org.acme.micrometer.ExampleResource" \ -Dpath="/" \ -Dextensions="resteasy,micrometer-registry-prometheus" cd micrometer-quickstart @@ -85,15 +95,12 @@ This will add the following to your `pom.xml`: == Writing the application -The application consists of a single class that implements an algorithm for checking whether a number is prime. -This algorithm is exposed over a REST interface. With the Micrometer extension enabled, metrics for all http -server requests are collected automatically. - -We do want to add a few other metrics to demonstrate how those types work: +Micrometer provides an API that allows you to construct your own custom metrics. The most common types of +meters supported by monitoring systems are gauges, counters, and summaries. The following sections build +an example endpoint, and observes endpoint behavior using these basic meter types. -* A counter will be incremented for every prime number discovered -* A gauge will store the highest prime number discovered -* A timer will record the time spent testing if ia number is prime. +To register meters, you need a reference to a `MeterRegistry`, which is configured and maintained by the Micrometer +extension. The `MeterRegistry` can be injected into your application as follows: [source,java] ---- @@ -105,183 +112,291 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.concurrent.atomic.LongAccumulator; -import java.util.function.Supplier; -@Path("/") -public class PrimeNumberResource { +@Path("/example") +@Produces("text/plain") +public class ExampleResource { - private final LongAccumulator highestPrime = new LongAccumulator(Long::max, 0); private final MeterRegistry registry; - PrimeNumberResource(MeterRegistry registry) { + ExampleResource(MeterRegistry registry) { this.registry = registry; + } +} +---- + +Micrometer maintains an internal mapping between unique metric identifier and tag combinations and specific meter +instances. Using `register`, `counter`, or other methods to increment counters or record values does not create +a new instance of a meter unless that combination of identifier and tag/label values hasn't been seen before. - // Create a gauge that uses the highestPrimeNumberSoFar method - // to obtain the highest observed prime number - registry.gauge("prime.number.max", this, - PrimeNumberResource::highestObservedPrimeNumber); +=== Gauges + +Gauges measure a value that can increase or decrease over time, like the speedometer on a car. Gauges can be +useful when monitoring the statistics for a cache or collection. Consider the following simple example that +observes the size of a list: + +[source,java] +---- + LinkedList list = new LinkedList<>(); + + // Update the constructor to create the gauge + ExampleResource(MeterRegistry registry) { + this.registry = registry; + registry.gaugeCollectionSize("example.list.size", Tags.empty(), list); } @GET - @Path("/{number}") - @Produces(MediaType.TEXT_PLAIN) + @Path("gauge/{number}") + public Long checkListSize(@PathParam("number") long number) { + if (number == 2 || number % 2 == 0) { + // add even numbers to the list + list.add(number); + } else { + // remove items from the list for odd numbers + try { + number = list.removeFirst(); + } catch (NoSuchElementException nse) { + number = 0; + } + } + return number; + } +---- + +Note that even numbers are added to the list, and odd numbers remove an element from the list. Try the following +sequence and look for `example_list_size` in the plain text output: + +[source,shell] +---- +# Compile and run the app in dev mode: +./mvnw compile quarkus:dev + +curl http://localhost:8080/example/gauge/1 +curl http://localhost:8080/example/gauge/2 +curl http://localhost:8080/example/gauge/4 +curl http://localhost:8080/q/metrics +curl http://localhost:8080/example/gauge/6 +curl http://localhost:8080/example/gauge/5 +curl http://localhost:8080/example/gauge/7 +curl http://localhost:8080/q/metrics +---- + +It is important to note that gauges are sampled rather than set; there is no record of how the value associated with a +gauge might have changed between measurements. In this example, the size of the list is observed when the Prometheus +endpoint is visited. + +Micrometer provides a few additional mechanisms for creating gauges. Note that Micrometer does not create strong +references to the objects it observes by default. Depending on the registry, Micrometer either omits gauges that observe +objects that have been garbage-collected entirely or uses `NaN` (not a number) as the observed value. + +When should you use a Gauge? Only if you can't use something else. Never gauge something you can count. Gauges can be +less straight-forward to use than counters. If what you are measuring can be counted (because the value always +increments), use a counter instead. + +=== Counters + +Counters are used to measure values that only increase. In the example below, you will count the number of times you +test a number to see if it is prime: + +[source,java] +---- + @GET + @Path("prime/{number}") public String checkIfPrime(@PathParam("number") long number) { if (number < 1) { return "Only natural numbers can be prime numbers."; } - if (number == 1) { - return "1 is not prime."; - } - if (number == 2) { - return "2 is prime."; - } - if (number % 2 == 0) { - return number + " is not prime, it is divisible by 2."; + if (number == 1 || number == 2 || number % 2 == 0) { + return number + " is not prime."; } - Supplier supplier = () -> { - for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { - if (number % i == 0) { - return number + " is not prime, is divisible by " + i + "."; - } - } - highestPrime.accumulate(number); + if ( testPrimeNumber(number) ) { return number + " is prime."; - }; - - return registry.timer("prime.number.test").wrap(supplier).get(); + } else { + return number + " is not prime."; + } } - /** - * This method is called by the registered {@code highest.prime.number} gauge. - * @return the highest observed prime value - */ - long highestObservedPrimeNumber() { - return highestPrime.get(); + protected boolean testPrimeNumber(long number) { + // Count the number of times we test for a prime number + registry.counter("example.prime.number").increment(); + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + if (number % i == 0) { + return false; + } + } + return true; } -} ---- -== Running and using the application +It might be tempting to add a label or tag to the counter indicating what value was checked, but remember that each +unique combination of metric name (`example.prime.number`) and label value produces a unique time series. Using an +unbounded set of data as label values can lead to a "cardinality explosion", an exponential increase in the creation +of new time series. -To run the microservice in dev mode, use `./mvnw clean compile quarkus:dev` +[NOTE] +==== +Label and tag can be used interchangably. You may also see "attribute" used in this context in some documentation. +The gist is each that each label or tag or attribute defines an additional bit of information associated with the +single numerical measurement that helps you classify, group, or aggregate the measured value later. The Micrometer API +uses `Tag` as the mechanism for specifying this additional data. +==== -=== Generate some values for the metrics +It is possible to add a tag that would convey a little more information, however. Let's adjust our code, and move +the counter to add some tags to convey additional information. -First, ask the endpoint whether some numbers are prime numbers. +[source,java] +---- + @GET + @Path("prime/{number}") + public String checkIfPrime(@PathParam("number") long number) { + if (number < 1) { + registry.counter("example.prime.number", "type", "not-natural").increment(); + return "Only natural numbers can be prime numbers."; + } + if (number == 1 ) { + registry.counter("example.prime.number", "type", "one").increment(); + return number + " is not prime."; + } + if (number == 2 || number % 2 == 0) { + registry.counter("example.prime.number", "type", "even").increment(); + return number + " is not prime."; + } -[source,bash] + if ( testPrimeNumber(number) ) { + registry.counter("example.prime.number", "type", "prime").increment(); + return number + " is prime."; + } else { + registry.counter("example.prime.number", "type", "not-prime").increment(); + return number + " is not prime."; + } + } + + protected boolean testPrimeNumber(long number) { + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + if (number % i == 0) { + return false; + } + } + return true; + } +---- + +Looking at the data produced by this counter, you can tell how often a negative number was checked, or the number one, +or an even number, and so on. Try the following sequence and look for `example_prime_number_total` in the plain text +output. Note that the `_total` suffix is added when Micrometer applies Prometheus naming conventions to +`example.prime.number`, the originally specified counter name. + +[source,shell] ---- -curl localhost:8080/350 +# If you did not leave quarkus running in dev mode, start it again: +./mvnw compile quarkus:dev + +curl http://localhost:8080/example/prime/-1 +curl http://localhost:8080/example/prime/0 +curl http://localhost:8080/example/prime/1 +curl http://localhost:8080/example/prime/2 +curl http://localhost:8080/example/prime/3 +curl http://localhost:8080/example/prime/15 +curl http://localhost:8080/q/metrics ---- -The application will respond that 350 is not a prime number because it can be divided by 2. +When should you use a counter? Only if you are doing something that can not be either timed (or summarized). +Counters only record a count, which may be all that is needed. However, if you want to understand more about how a +value is changing, a timer (when the base unit of measurement is time) or a distribution summary might be +more appropriate. -Now for some large prime number so that the test takes a bit more time: +=== Summaries and Timers -[source,bash] +Timers and distribution summaries in Micrometer are very similar. Both allow you to record an observed value, which +will be aggregated with other recorded values and stored as a sum. Micrometer also increments a counter to indicate the +number of measurements that have been recorded and tracks the maximum observed value (within a decaying interval). + +Distribution summaries are populated by calling the `record` method to record observed values, while timers provide +additional capabilities specific to working with time and measuring durations. For example, we can use a timer to +measure how long it takes to calculate prime numbers using one of the `record` methods that wraps the invocation of a +Supplier function: + +[source,java] +---- + protected boolean testPrimeNumber(long number) { + Timer timer = registry.timer("example.prime.number.test"); + return timer.record(() -> { + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + if (number % i == 0) { + return false; + } + } + return true; + }); + } ---- -curl localhost:8080/629521085409773 + +Micrometer will apply Prometheus conventions when emitting metrics for this timer. Prometheus measures time in seconds. +Micrometer converts measured durations into seconds and includes the unit in the metric name, per convention. After +visiting the prime endpoint a few more times, look in the plain text output for the following three entries: +`example_prime_number_test_seconds_count`, `example_prime_number_test_seconds_sum`, and +`example_prime_number_test_seconds_max`. + +[source,shell] ---- +# If you did not leave quarkus running in dev mode, start it again: +./mvnw compile quarkus:dev -The application will respond that 629521085409773 is a prime number. -If you want, try some more calls with numbers of your choice. +curl http://localhost:8080/example/prime/256 +curl http://localhost:8080/q/metrics +curl http://localhost:8080/example/prime/7919 +curl http://localhost:8080/q/metrics +---- -=== Review the generated metrics +Both timers and distribution summaries can be configured to emit additional statistics, like histogram data, +precomputed percentiles, or service level objective (SLO) boundaries. Note that the count, sum, and histogram data +can be re-aggregated across dimensions (or across a series of instances), while precomputed percentile values cannot. -To view the metrics, execute `curl localhost:8080/q/metrics/` +=== Review automatically generated metrics -Prometheus-formatted metrics will be returned in plain text in no particular order. +To view metrics, execute `curl localhost:8080/q/metrics/` -The application above has only one custom gauge that measures the time -spent determining whether or not a number is prime. The Micrometer extension -enables additional system, jvm, and http metrics. A subset of the collected metrics -are shown below. +The Micrometer extension automatically times HTTP server requests. Following Prometheus naming conventions for +timers, look for `http_server_requests_seconds_count`, `http_server_requests_seconds_sum`, and +`http_server_requests_seconds_max`. Dimensional labels have been added for the requested uri, the HTTP method +(GET, POST, etc.), the status code (200, 302, 404, etc.), and a more general outcome field. [source,text] ---- # HELP http_server_requests_seconds # TYPE http_server_requests_seconds summary -http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 4.0 -http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 0.041501773 +http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 1.0 +http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 # HELP http_server_requests_seconds_max # TYPE http_server_requests_seconds_max gauge -http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 0.038066359 -# HELP jvm_threads_peak_threads The peak live thread count since the Java virtual machine started or peak was reset -# TYPE jvm_threads_peak_threads gauge -jvm_threads_peak_threads 56.0 -# HELP http_server_connections_seconds_max -# TYPE http_server_connections_seconds_max gauge -http_server_connections_seconds_max 0.102580737 -# HELP http_server_connections_seconds -# TYPE http_server_connections_seconds summary -http_server_connections_seconds_active_count 5.0 -http_server_connections_seconds_duration_sum 0.175032815 -# HELP system_load_average_1m The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time -# TYPE system_load_average_1m gauge -system_load_average_1m 2.4638671875 -# HELP http_server_bytes_written_max -# TYPE http_server_bytes_written_max gauge -http_server_bytes_written_max 39.0 -# HELP http_server_bytes_written -# TYPE http_server_bytes_written summary -http_server_bytes_written_count 4.0 -http_server_bytes_written_sum 99.0 -# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine -# TYPE jvm_classes_loaded_classes gauge -jvm_classes_loaded_classes 9341.0 -# HELP prime_number_max -# TYPE prime_number_max gauge -prime_number_max 887.0 -# HELP jvm_info JVM version info -# TYPE jvm_info gauge -jvm_info{runtime="OpenJDK Runtime Environment",vendor="Oracle Corporation",version="13.0.2+8",} 1.0 -# HELP jvm_classes_unloaded_classes_total The total number of classes unloaded since the Java virtual machine has started execution -# TYPE jvm_classes_unloaded_classes_total counter -jvm_classes_unloaded_classes_total 28.0 -# HELP prime_number_test_seconds -# TYPE prime_number_test_seconds summary -prime_number_test_seconds_count 3.0 -prime_number_test_seconds_sum 1.94771E-4 -# HELP prime_number_test_seconds_max -# TYPE prime_number_test_seconds_max gauge -prime_number_test_seconds_max 1.76162E-4 -# HELP http_server_bytes_read -# TYPE http_server_bytes_read summary -http_server_bytes_read_count 4.0 -http_server_bytes_read_sum 0.0 -# HELP http_server_bytes_read_max -# TYPE http_server_bytes_read_max gauge -http_server_bytes_read_max 0.0 -# HELP jvm_threads_states_threads The current number of threads having NEW state -# TYPE jvm_threads_states_threads gauge -jvm_threads_states_threads{state="runnable",} 37.0 -jvm_threads_states_threads{state="blocked",} 0.0 -jvm_threads_states_threads{state="waiting",} 15.0 -jvm_threads_states_threads{state="timed-waiting",} 4.0 -jvm_threads_states_threads{state="new",} 0.0 -jvm_threads_states_threads{state="terminated",} 0.0 -# HELP jvm_buffer_memory_used_bytes An estimate of the memory that the Java virtual machine is using for this buffer pool -# TYPE jvm_buffer_memory_used_bytes gauge -jvm_buffer_memory_used_bytes{id="mapped",} 0.0 -jvm_buffer_memory_used_bytes{id="direct",} 149521.0 -# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use -# TYPE jvm_memory_committed_bytes gauge -jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'profiled nmethods'",} 1.1403264E7 -jvm_memory_committed_bytes{area="heap",id="G1 Survivor Space",} 4194304.0 -jvm_memory_committed_bytes{area="heap",id="G1 Old Gen",} 9.2274688E7 -jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 4.9803264E7 -jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-nmethods'",} 2555904.0 -jvm_memory_committed_bytes{area="heap",id="G1 Eden Space",} 6.9206016E7 -jvm_memory_committed_bytes{area="nonheap",id="Compressed Class Space",} 6815744.0 -jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-profiled nmethods'",} 2555904.0 +http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 +# ---- Note that metrics appear lazily, you often won't see any data for your endpoint until something tries to access it, etc. +.Ignoring endpoints + +You can disable measurement of HTTP endpoints using the `quarkus.micrometer.binder.http-server.ignore-patterns` +property. This property accepts a comma-separated list of simple regex match patterns identifying URI paths that should +be ignored. For example, setting `quarkus.micrometer.binder.http-server.ignore-patterns=/example/prime/[0-9]+` will +ignore a request to `http://localhost:8080/example/prime/7919`. A request to `http://localhost:8080/example/gauge/7919` +would still be measured. + +.URI templates + +The micrometer extension will make a best effort at representing URIs containing path parameters in templated form. +Using examples from above, a request to `http://localhost:8080/example/prime/7919` should appear as an attribute of +`http_server_requests_seconds_*` metrics with a value of `uri=/example/prime/{number}`. + +Use the `quarkus.micrometer.binder.http-server.match-patterns` property if the correct URL can not be determined. This +property accepts a comma-separated list defining an association between a simple regex match pattern and a replacement +string. For example, setting +`quarkus.micrometer.binder.http-server.match-patterns=/example/prime/[0-9]+=/example/{jellybeans}` would use the value +`/example/{jellybeans}` for the uri attribute any time the requested uri matches `/example/prime/[0-9]+`. + == Using MeterFilter to configure metrics Micrometer uses `MeterFilter` instances to customize the metrics emitted by `MeterRegistry` instances. @@ -337,6 +452,17 @@ public class CustomConfiguration { In this example, a singleton CDI bean will produce two different `MeterFilter` beans. One will be applied only to Prometheus `MeterRegistry` instances (using the `@MeterFilterConstraint` qualifier), and another will be applied to all `MeterRegistry` instances. An application configuration property is also injected and used as a tag value. +Additional examples of MeterFilters can be found in the +link:https://micrometer.io/docs/concepts[official documentation]. + +== Does Micrometer support annotations? + +Micrometer does define two annotations, `@Counted` and `@Timed`, that can be added to methods. The `@Timed` annotation +will wrap the execution of a method and will emit the following tags in addition to any tags defined on the +annotation itself: class, method, and exception (either "none" or the simple class name of a detected exception). + +Using annotations is limited, as you can't dynamically assign meaningful tag values. Also note that many methods, e.g. +REST endpoint methods or Vert.x Routes, are counted and timed by the micrometer extension out of the box. == Using other Registry implementations @@ -406,24 +532,25 @@ that are integrated with the Quarkus configuration model. If you use the MicroProfile Metrics API in your application, the Micrometer extension will create an adaptive layer to map those metrics into the Micrometer registry. Note that naming conventions between the two -systems will change, but you can use MeterFilters to remap names or tags to what your dashboards require. +systems is different, so the metrics that are emitted when using MP Metrics with Micrometer will change. +You can use a `MeterFilter` to remap names or tags according to your conventions. [source,java] ---- @Produces @Singleton public MeterFilter renameApplicationMeters() { - final String targetMetric = PrimeResource.class.getName() + ".highestPrimeNumberSoFar"; + final String targetMetric = MPResourceClass.class.getName() + ".mpAnnotatedMethodName"; return MeterFilter() { @Override public Meter.Id map(Meter.Id id) { - // rename the specified metric (remove package), and drop the scope tag - // you could also use this to prepend a scope tag (application, base, vendor, if present) to the metric name if (id.getName().equals(targetMetric)) { + // Drop the scope tag (MP Registry type: application, vendor, base) List tags = id.getTags().stream().filter(x -> !"scope".equals(x.getKey())) .collect(Collectors.toList()); - return id.withName("highestPrimeNumberSoFar").replaceTags(tags); + // rename the metric + return id.withName("my.metric.name").replaceTags(tags); } return id; } @@ -431,6 +558,16 @@ public MeterFilter renameApplicationMeters() { } ---- +Ensure the following dependency is present in your `pom.xml` file if you require the Microprofile Metrics API: + +[source,xml] +---- + + org.eclipse.microprofile.metrics + microprofile-metrics-api + +---- + NOTE: The MP Metrics API compatibility layer will be moved to a different extension in the future. == Configuration Reference diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc index bb1d3e2d90ca5c..66dcb011f6ccb1 100644 --- a/docs/src/main/asciidoc/mongodb-panache.adoc +++ b/docs/src/main/asciidoc/mongodb-panache.adoc @@ -656,7 +656,11 @@ quarkus.log.category."io.quarkus.mongodb.panache.runtime".level=DEBUG == Transactions -WARNING: MongoDB offers ACID transactions since version 4.0. MongoDB with Panache doesn't provide support for them. +MongoDB offers ACID transactions since version 4.0. + +To use them with MongoDB with Panache you need to annotate the method that starts the transaction with the `@Transactional` annotation. + +WARNING: Transaction support inside MongoDB with Panache is still experimental. == Custom IDs @@ -890,6 +894,8 @@ public Multi streamPersons() { TIP: `@SseElementType(MediaType.APPLICATION_JSON)` tells RESTEasy to serialize the object in JSON. +WARNING: Transactions are not supported for Reactive Entities and Repositories. + == Mocking === Using the active-record pattern diff --git a/docs/src/main/asciidoc/openapi-swaggerui.adoc b/docs/src/main/asciidoc/openapi-swaggerui.adoc index 0b973fc5d1cb18..289e4d6f0e2a75 100644 --- a/docs/src/main/asciidoc/openapi-swaggerui.adoc +++ b/docs/src/main/asciidoc/openapi-swaggerui.adoc @@ -173,8 +173,10 @@ public class FruitResourceTest { == Expose OpenAPI Specifications -Quarkus proposes a `smallrye-openapi` extension compliant with the https://github.com/eclipse/microprofile-open-api/[Eclipse MicroProfile OpenAPI] -specification in order to generate your API https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md[OpenAPI v3 specification]. +Quarkus provides the https://github.com/smallrye/smallrye-open-api/[Smallrye OpenAPI] extension compliant with the +https://github.com/eclipse/microprofile-open-api/[MicroProfile OpenAPI] +specification in order to generate your API +https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md[OpenAPI v3 specification]. You just need to add the `openapi` extension to your Quarkus application: @@ -439,6 +441,7 @@ Now you do not need to use the `@Operation` annotation. While generating the doc |Use the class name plus the method name. |=== +[[dev-mode]]] == Use Swagger UI for development When building APIs, developers want to test them quickly. https://swagger.io/tools/swagger-ui/[Swagger UI] is a great tool diff --git a/docs/src/main/asciidoc/opentracing.adoc b/docs/src/main/asciidoc/opentracing.adoc index faa47e513156c1..5dd2f855ab6822 100644 --- a/docs/src/main/asciidoc/opentracing.adoc +++ b/docs/src/main/asciidoc/opentracing.adoc @@ -100,7 +100,9 @@ public class TracedResource { ---- Notice that there is no tracing specific code included in the application. By default, requests sent to this -endpoint will be traced without any code changes being required. It is also possible to enhance the tracing information. For more information on this, please see the https://github.com/eclipse/microprofile-opentracing/blob/master/spec/src/main/asciidoc/microprofile-opentracing.asciidoc[MicroProfile OpenTracing specification]. +endpoint will be traced without any code changes being required. It is also possible to enhance the tracing information. +This can be achieved by https://github.com/smallrye/smallrye-opentracing/[SmallRye OpenTracing] an implementation of +https://github.com/eclipse/microprofile-opentracing/[MicroProfile OpenTracing]. === Create the configuration @@ -168,7 +170,7 @@ Hit `CTRL+C` to stop the application. == Tracing additional methods REST endpoints are automatically traced. -If you need to trace additional methods, you can use the `org.eclipse.microprofile.opentracing.Traced` annotation at class or method level. +If you need to trace additional methods, you can add the `org.eclipse.microprofile.opentracing.Traced` annotation to CDI bean classes or their non-private methods. This can be useful to trace incoming requests from non-REST calls (like request coming from a message) or to create spans inside a trace. @@ -190,7 +192,7 @@ public class FrancophoneService { } ---- -NOTE: the best way to add OpenTracing capability to reactive messaging based applications is by adding the `Traced` annotation to all incoming methods. +NOTE: The best way to add OpenTracing capability to reactive messaging based applications is by adding the `Traced` annotation to all incoming methods. == Additional instrumentation diff --git a/docs/src/main/asciidoc/platform.adoc b/docs/src/main/asciidoc/platform.adoc index 084344e1ab804f..f137389f95b1d8 100644 --- a/docs/src/main/asciidoc/platform.adoc +++ b/docs/src/main/asciidoc/platform.adoc @@ -90,7 +90,7 @@ The platform descriptor will normally be generated using a Maven plugin, e.g. A Quarkus platform may provide its own default values for some of the configuration options. -Quarkus is using https://github.com/eclipse/microprofile-config[Eclipse MicroProfile Config] for wiring application configuration. A Quarkus platform may be used as another source of configuration in the hierarchy of the configuration sources dominated by the application's `application.properties`. +Quarkus is using https://github.com/smallrye/smallrye-config[SmallRye Config] for wiring application configuration. A Quarkus platform may be used as another source of configuration in the hierarchy of the configuration sources dominated by the application's `application.properties`. To provide platform-specific defaults, the platform needs to include a dependency version constraint in its BOM for a properties artifact whose coordinates follow the following naming convention: @@ -188,4 +188,4 @@ For example, the `quarkus-universe-bom` platform is based on the `quarkus-bom` p ---- -That way, the `quarkus-universe-bom` platform properties will appear before the `quarkus-bom` platform properties and so will be dominating during merging. \ No newline at end of file +That way, the `quarkus-universe-bom` platform properties will appear before the `quarkus-bom` platform properties and so will be dominating during merging. diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index ec15b22043500e..3d6ff364bdba4e 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -831,27 +831,26 @@ Template inheritance makes it possible to reuse template layouts. {#insert title}Default Title{/} <1> - {#insert body}No body!{/} <2> + {#insert}No body!{/} <2> ---- <1> `insert` sections are used to specify parts that could be overridden by a template that includes the given template. -<2> An `insert` section may define the default content that is rendered if not overridden. +<2> An `insert` section may define the default content that is rendered if not overridden. If no name parameter is supplied then the main block of the relevant `{#include}` section is used. .Template "detail" [source,html] ---- {#include base} <1> {#title}My Title{/title} <2> - {#body} -
- My body. -
- {/body} +
<3> + My body. +
{/include} ---- <1> `include` section is used to specify the extended template. <2> Nested blocks are used to specify the parts that should be overridden. +<3> The content of the main block is used for an `{#insert}` section with no name parameter specified. NOTE: Section blocks can also define an optional end tag - `{/title}`. diff --git a/docs/src/main/asciidoc/reactive-messaging-http.adoc b/docs/src/main/asciidoc/reactive-messaging-http.adoc index 2e653d4ee86f86..cca81460af084b 100644 --- a/docs/src/main/asciidoc/reactive-messaging-http.adoc +++ b/docs/src/main/asciidoc/reactive-messaging-http.adoc @@ -7,7 +7,7 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize MicroProfile Reactive Messaging to +This guide demonstrates how your Quarkus application can utilize SmallRye Reactive Messaging to consume and produce HTTP messages. == Prerequisites @@ -387,7 +387,7 @@ mp.messaging.incoming..buffer-size=3 ---- === Reactive Messaging -This extension utilizes MicroProfile Reactive Messaging to build data streaming applications. +This extension utilizes SmallRye Reactive Messaging to build data streaming applications. If you did the Kafka or AMQP quickstart, you have realized that it's the same code. The only difference is the connector configuration. diff --git a/docs/src/main/asciidoc/rest-client-multipart.adoc b/docs/src/main/asciidoc/rest-client-multipart.adoc index f84ff56f4de4ce..086b59161ca962 100644 --- a/docs/src/main/asciidoc/rest-client-multipart.adoc +++ b/docs/src/main/asciidoc/rest-client-multipart.adoc @@ -10,7 +10,7 @@ include::./attributes.adoc[] RESTEasy has rich support for the `multipart/*` and `multipart/form-data` mime types. The multipart mime format is used to pass lists of content bodies. Multiple content bodies are embedded in one message. `multipart/form-data` is often found in web application HTML Form documents and is generally used to upload files. The form-data format is the same as other multipart formats, except that each inlined piece of content has a name associated with it. -This guide explains how to use the MicroProfile REST Client with Multipart in order to interact with REST APIs +This guide explains how to use the RESTEasy REST Client with Multipart in order to interact with REST APIs requiring `multipart/form-data` content-type with very little effort. == Prerequisites @@ -93,7 +93,7 @@ The purpose of the annotations in the code above is the following: == Create the interface -Using the MicroProfile REST Client is as simple as creating an interface using the proper JAX-RS and MicroProfile annotations. In our case the interface should be created at `src/main/java/org/acme/rest/client/multipart/MultipartService.java` and have the following content: +Using the RESTEasy REST Client is as simple as creating an interface using the proper JAX-RS and MicroProfile annotations. In our case the interface should be created at `src/main/java/org/acme/rest/client/multipart/MultipartService.java` and have the following content: [source, java] ---- @@ -298,5 +298,5 @@ You can also generate the native executable with `./mvnw clean package -Pnative` == Further reading - * link:https://download.eclipse.org/microprofile/microprofile-rest-client-1.4.1/microprofile-rest-client-1.4.1.html[MicroProfile Rest Client specification] - * link:https://docs.jboss.org/resteasy/docs/4.5.6.Final/userguide/html/Multipart.html[RESTEasy Multipart Provider] +* link:https://docs.jboss.org/resteasy/docs/4.5.6.Final/userguide/html/Multipart.html[RESTEasy Multipart Provider] +* link:https://download.eclipse.org/microprofile/microprofile-rest-client-1.4.1/microprofile-rest-client-1.4.1.html[MicroProfile Rest Client specification] diff --git a/docs/src/main/asciidoc/rest-client.adoc b/docs/src/main/asciidoc/rest-client.adoc index b16d5a665632e7..e3336412eb626f 100644 --- a/docs/src/main/asciidoc/rest-client.adoc +++ b/docs/src/main/asciidoc/rest-client.adoc @@ -7,7 +7,7 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -This guide explains how to use the MicroProfile REST Client in order to interact with REST APIs +This guide explains how to use the RESTEasy REST Client in order to interact with REST APIs with very little effort. TIP: there is another guide if you need to write server link:rest-json[JSON REST APIs]. @@ -108,7 +108,7 @@ but it suffices for the purposes of this guide. == Create the interface -Using the MicroProfile REST Client is as simple as creating an interface using the proper JAX-RS and MicroProfile annotations. In our case the interface should be created at `src/main/java/org/acme/rest/client/CountriesService.java` and have the following content: +Using the RESTEasy REST Client is as simple as creating an interface using the proper JAX-RS and MicroProfile annotations. In our case the interface should be created at `src/main/java/org/acme/rest/client/CountriesService.java` and have the following content: [source, java] ---- @@ -631,7 +631,7 @@ public class WiremockCountries implements QuarkusTestResourceLifecycleManager { stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://restcountries.eu/rest"))); // <5> - return Collections.singletonMap("org.acme.getting.started.country.CountriesService/mp-rest/url", wireMockServer.baseUrl()); // <6> + return Collections.singletonMap("org.acme.rest.client.CountriesService/mp-rest/url", wireMockServer.baseUrl()); // <6> } @Override diff --git a/docs/src/main/asciidoc/resteasy-reactive.adoc b/docs/src/main/asciidoc/resteasy-reactive.adoc index 15d7ed851f6aef..d759f757f32f64 100644 --- a/docs/src/main/asciidoc/resteasy-reactive.adoc +++ b/docs/src/main/asciidoc/resteasy-reactive.adoc @@ -681,6 +681,12 @@ method takes parameters of the following type: |link:{jaxrsapi}/javax/ws/rs/sse/Sse.html[`Sse`] |Advanced: Complex SSE use-cases +|link:{vertxapi}/io/vertx/core/http/HttpServerRequest.html[`HttpServerRequest`] +|Advanced: Vert.x HTTP Request + +|link:{vertxapi}/io/vertx/core/http/HttpServerResponse.html[`HttpServerResponse`] +|Advanced: Vert.x HTTP Response + |=== For example, here is how you can return the name of the currently logged-in user: @@ -1150,6 +1156,19 @@ class Filters { } ---- +[IMPORTANT] +==== +Request filters are usually executed on the same thread that the method that handles the request will be executed. +That means that if the method servicing the request is annotated with `@Blocking`, then the filters will also be run +on the worker thread. +If the method is annotated with `@NonBlocking` (or is not annotated at all), then the filters will also be run +on the same event-loop thread. + +If however a filter needs to be run on the event-loop despite the fact that the method servicing the request will be +run on a worker thread, then `@ServerRequestFilter(nonBlocking=true)` can be used. +Note however, that these filters need to be run before **any** filter that does not use that setting and would run on a worker thread. +==== + Similarly, response filters can be declared with the `@ServerResponseFilter` annotation: [source,java] @@ -1827,4 +1846,3 @@ To change this behavior, set the `quarkus.rest.client.scope` property to the ful - a few things that don't make sense for a non-blocking implementations, such as setting the `ExecutorService`, don't work It is important to note that the `quarkus-rest-client` extension may not work properly with RESTEasy Reactive. - diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index 07b588c00a78b3..adbe3cc353f59b 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -7,6 +7,21 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] +== Quarkus Security Dependency + +`io.quarkus:quarkus-security` module contains the core Quarkus security classes. + +In most cases it does not have to be added directly to your project's `pom.xml` as it is already provided by all of the security extensions. +However if you need to write your own custom security code (for example, register a <>) or use <> libraries, then please make sure it is included: + +[source,xml] +---- + + io.quarkus + quarkus-security + +---- + == HttpAuthenticationMechanism Customization One can customize `HttpAuthenticationMechanism` by registering a CDI implementation bean. @@ -146,6 +161,7 @@ If more than one custom `SecurityIdentityAugmentor` is registered then they will You can enforce the order by implementing a default `SecurityIdentityAugmentor#priority` method. Augmentors with higher priorities will be invoked first. === +[[jaxrs-security-context]] == Custom JAX-RS SecurityContext If you use JAX-RS `ContainerRequestFilter` to set a custom JAX-RS `SecurityContext` then make sure `ContainerRequestFilter` runs in the JAX-RS pre-match phase by adding a `@PreMatching` annotation to it for this custom security context to be linked with Quarkus `SecurityIdentity`, for example: @@ -235,6 +251,7 @@ configuration to register the "SunRsaSign" and "SunJCE" security providers: quarkus.security.security-providers=SunRsaSign,SunJCE ---- +[[bouncy-castle]] === BouncyCastle If you need to register an `org.bouncycastle.jce.provider.BouncyCastleProvider` JCE provider then please set a `BC` provider name: diff --git a/docs/src/main/asciidoc/security-jwt.adoc b/docs/src/main/asciidoc/security-jwt.adoc index b8dd9b340296bf..b1d3768dd4b3b2 100644 --- a/docs/src/main/asciidoc/security-jwt.adoc +++ b/docs/src/main/asciidoc/security-jwt.adoc @@ -9,7 +9,9 @@ include::./attributes.adoc[] :extension-name: SmallRye JWT :mp-jwt: MicroProfile JWT RBAC -This guide explains how your Quarkus application can utilize MicroProfile JWT (MP JWT) to verify https://tools.ietf.org/html/rfc7519[JSON Web Token]s, represent them as MP JWT `org.eclipse.microprofile.jwt.JsonWebToken` and provide secured access to the Quarkus HTTP endpoints using Bearer Token Authorization and https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control]. +This guide explains how your Quarkus application can utilize https://github.com/smallrye/smallrye-jwt/[SmallRye JWT] +to verify https://tools.ietf.org/html/rfc7519[JSON Web Token]s, represent them as MicroProfile JWT `org.eclipse.microprofile.jwt.JsonWebToken` +and provide secured access to the Quarkus HTTP endpoints using Bearer Token Authorization and https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control]. NOTE: Quarkus OpenId Connect extension also supports Bearer Token Authorization and uses `smallrye-jwt` to represent the bearer tokens as `JsonWebToken`, please read the link:security-openid-connect[Using OpenID Connect to Protect Service Applications] guide for more information. OpenId Connect extension has to be used if the Quarkus application needs to authenticate the users using OIDC Authorization Code Flow, please read link:security-openid-connect-web-authentication[Using OpenID Connect to Protect Web Applications] guide for more information. @@ -800,6 +802,43 @@ public class TestJWTCallerPrincipalFactory extends JWTCallerPrincipalFactory { } ---- +== Token Propagation + +Please see link:security-openid-connect-client#token-propagation[Token Propagation] section about the Bearer access token propagation to the downstream services. + +[[integration-testing]] +== Testing + +=== Wiremock + +If you configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the link:security-openid-connect#integration-testing[OpenId Connect Bearer Token Integration testing] `Wiremock` section but only change the `application.properties` to use MP JWT configuration properties instead: + +[source, properties] +---- +# keycloak.url is set by OidcWiremockTestResource +mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs +mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus + +# required to sign the tokens +smallrye.jwt.sign.key.location=privateKey.jwk +---- + +It will ensure that the `smallrye-jwt` remote key resolution code also works as expected. + +=== Local Public Key + +You can use the same approach as described in the link:security-openid-connect#integration-testing[OpenId Connect Bearer Token Integration testing] `Local Public Key` section but only change the `application.properties` to use MP JWT configuration properties instead: + +[source, properties] +---- +mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB +# set it to the issuer value which is used to generate the tokens +mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus + +# required to sign the tokens +smallrye.jwt.sign.key.location=privateKey.pem +---- + [[generate-jwt-tokens]] == Generate JWT tokens with SmallRye JWT diff --git a/docs/src/main/asciidoc/security-keycloak-authorization.adoc b/docs/src/main/asciidoc/security-keycloak-authorization.adoc index 4121457d565065..b38440546bae33 100644 --- a/docs/src/main/asciidoc/security-keycloak-authorization.adoc +++ b/docs/src/main/asciidoc/security-keycloak-authorization.adoc @@ -308,6 +308,9 @@ injecting a `SecurityIdentity` instance in your beans, you are allowed to check [source,java] ---- +import io.quarkus.security.identity.SecurityIdentity; +import io.smallrye.mutiny.Uni; + @Path("/api/protected") public class ProtectedResource { @@ -316,11 +319,11 @@ public class ProtectedResource { @GET - public CompletionStage> get() { - return identity.checkPermission(new AuthPermission("{resource_name}")) + public Uni> get() { + return identity.checkPermission(new AuthPermission("{resource_name}")).onItem() .transform(granted -> { if (granted) { - return CompletableFuture.completedFuture(doGetState()); + return identity.getAttribute("permissions"); } throw new ForbiddenException(); }); diff --git a/docs/src/main/asciidoc/security-oauth2.adoc b/docs/src/main/asciidoc/security-oauth2.adoc index eccfe5c01a36c3..817586a8d801d1 100644 --- a/docs/src/main/asciidoc/security-oauth2.adoc +++ b/docs/src/main/asciidoc/security-oauth2.adoc @@ -16,7 +16,7 @@ It can be used to implement an application authentication mechanism based on tok This extension provides a light-weight support for using the opaque Bearer Tokens and validating them by calling an introspection endpoint. -If the OAuth2 Authentication server provides JWT Bearer Tokens then you should consider using either link:security-openid-connect[OpenId Connect] or link:security-jwt[MicroProfile JWT RBAC] extensions instead. +If the OAuth2 Authentication server provides JWT Bearer Tokens then you should consider using either link:security-openid-connect[OpenId Connect] or link:security-jwt[SmallRye JWT] extensions instead. OpenId Connect extension has to be used if the Quarkus application needs to authenticate the users using OIDC Authorization Code Flow, please read link:security-openid-connect-web-authentication[Using OpenID Connect to Protect Web Applications] guide for more information. include::./status-include.adoc[] diff --git a/docs/src/main/asciidoc/security-openid-connect-client.adoc b/docs/src/main/asciidoc/security-openid-connect-client.adoc index ae62a005c7bc5e..c0b0657569fa23 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client.adoc @@ -10,8 +10,8 @@ include::./attributes.adoc[] This guide explains how to use: - * `quarkus-oidc-client` and `quarkus-oidc-client-filter` extensions to acquire and refresh access tokens from OpenId Connect and OAuth 2.0 compliant Authorization Servers such as https://www.keycloak.org/about.html[Keycloak] - * `quarkus-oidc-token-propagation` extension to propagate the current bearer or authorization code flow access tokens + - `quarkus-oidc-client` and `quarkus-oidc-client-filter` extensions to acquire and refresh access tokens from OpenId Connect and OAuth 2.0 compliant Authorization Servers such as https://www.keycloak.org/about.html[Keycloak] + - `quarkus-oidc-token-propagation` extension to propagate the current bearer or authorization code flow access tokens The access tokens managed by these extensions can be used as HTTP Authorization Bearer tokens to access the remote services. @@ -25,7 +25,7 @@ Here is how `OidcClient` can be configured to use the `client_credentials` grant [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus2/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret ---- @@ -34,7 +34,7 @@ Here is how `OidcClient` can be configured to use the `password` grant: [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus2/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=password @@ -167,7 +167,7 @@ Please note that some OpenId Connect Providers will not return a refresh token i ---- quarkus.oidc-client.client-enabled=false -quarkus.oidc-client.jwt-secret.auth-server-url=${keycloak.url}/realms/quarkus2/ +quarkus.oidc-client.jwt-secret.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.jwt-secret.client-id=quarkus-app quarkus.oidc-client.jwt-secret.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow ---- @@ -273,7 +273,7 @@ All the https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthenticati [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=mysecret ---- @@ -282,7 +282,7 @@ quarkus.oidc-client.credentials.secret=mysecret [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.client-secret.value=mysecret quarkus.oidc-client.credentials.client-secret.method=post @@ -292,7 +292,7 @@ quarkus.oidc-client.credentials.client-secret.method=post [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow ---- @@ -301,7 +301,7 @@ quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-Es [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem ---- @@ -310,7 +310,7 @@ quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem [source,properties] ---- -quarkus.oidc-client.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-store-file=keystore.jks quarkus.oidc-client.credentials.jwt.key-store-password=mypassword @@ -320,6 +320,127 @@ quarkus.oidc-client.credentials.jwt.key-id=mykey Using `private_key_jwt` or `private_key_jwt` authentication methods ensures that no client secret goes over the wire. +[[integration-testing-oidc-client]] +=== Testing + +Add the following dependencies to your test project: + +[source,xml] +---- + + io.quarkus + quarkus-junit5 + test + + + com.github.tomakehurst + wiremock-jre8 + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + +---- + +Write Wiremock based `QuarkusTestResourceLifecycleManager`, for example: +[source, java] +---- +package io.quarkus.it.keycloak; + +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; + +import java.util.HashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.Options.ChunkedEncodingPolicy; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycleManager { + + private WireMockServer server; + + @Override + public Map start() { + + server = new WireMockServer(wireMockConfig().dynamicPort().useChunkedTransferEncoding(ChunkedEncodingPolicy.NEVER)); + server.start(); + + server.stubFor(WireMock.post("/tokens") + .withRequestBody(matching("grant_type=password&username=alice&password=alice")) + .willReturn(WireMock + .aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); + server.stubFor(WireMock.post("/tokens") + .withRequestBody(matching("grant_type=refresh_token&refresh_token=refresh_token_1")) + .willReturn(WireMock + .aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "{\"access_token\":\"access_token_2\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); + + + Map conf = new HashMap<>(); + conf.put("quarkus.oidc-client.auth-server-url", server.baseUrl()); + conf.put("keycloak-url", server.baseUrl()); + return conf; + } + + @Override + public synchronized void stop() { + if (server != null) { + server.stop(); + server = null; + } + } +} +---- + +Prepare the REST test endpoints, you can have the test frontend endpoint which uses the injected MP REST client with a registered OidcClient filter to invoke on the downstream endpoint which echoes the token back, for example, see the `integration-tests/oidc-client-wiremock` in the `main` Quarkus repository. + +Set `application.properties`, for example: + +[source, properties] +---- +# Use 'keycloak.url' property set by the test KeycloakRealmResourceManager +quarkus.oidc-client.auth-server-url=${keycloak.url} +quarkus.oidc-client.discovery-enabled=false +quarkus.oidc-client.token-path=/tokens +quarkus.oidc-client.client-id=quarkus-app +quarkus.oidc-client.credentials.secret=secret +quarkus.oidc-client.grant.type=password +quarkus.oidc-client.grant-options.password.username=alice +quarkus.oidc-client.grant-options.password.password=alice +---- + +and finally write the test code. Given the Wiremock-based resource above, the first test invocation should return `access_token_1` access token which will expire in 4 seconds. Use `awaitility` to wait for about 5 seconds, and now the next test invocation should return `access_token_2` access token which confirms the expired `access_token_1` access token has been refreshed. + +== Token endpoint configuration + +By default the token endpoint address is discovered by adding a `/.well-known/openid-configuration` path to the configured `quarkus.oidc-client.auth-server-url`. + +Alternatively, if the discovery endpoint is not available or you would like to save on the discovery endpoint roundtrip, you can disable the discovery and configure the token endpoint address with a relative path value, for example: + +[source, properties] +---- +quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus +quarkus.oidc-client.discovery-enabled=false +# Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens +quarkus.oidc-client.token-path=/protocol/openid-connect/tokens +---- + [[token-propagation]] == Token Propagation in MicroProfile RestClient client filter @@ -430,6 +551,12 @@ smallrye.jwt.new-token.override-matching-claims=true This filter will be additionally enhanced in the future to support exchanging the access tokens before propagating them. +[[integration-testing-token-propagation]] +=== Testing + +You can generate the tokens as described in link:security-openid-connect#integration-testing[OpenId Connect Bearer Token Integration testing] section. +Prepare the REST test endpoints, you can have the test frontend endpoint which uses the injected MP REST client with a registered token propagation filter to invoke on the downstream endpoint, for example, see the `integration-tests/oidc-token-propagation` in the `main` Quarkus repository. + == References * link:security[Quarkus Security] 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 9ee4523d5e681f..7ada5b816e0fc2 100644 --- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc @@ -519,7 +519,7 @@ In such cases configuring Quarkus to recognize the original headers forwarded by === Google Cloud -You can have Quarkus OIDC `web-app` applications access **Google Cloud services** such as **BigQuery** on behalf of the currently authenticated users who have enabled OpendId Connect (Authorization Code Flow) permissions to such services in their Google Developer Consoles. +You can have Quarkus OIDC `web-app` applications access **Google Cloud services** such as **BigQuery** on behalf of the currently authenticated users who have enabled OpenID Connect (Authorization Code Flow) permissions to such services in their Google Developer Consoles. It is super easy to do with https://github.com/quarkiverse[Quarkiverse] https://github.com/quarkiverse/quarkiverse-google-cloud-services[Google Cloud Services], only add the https://github.com/quarkiverse/quarkiverse-google-cloud-services/releases/latest[latest tag] service dependency, for example: @@ -544,9 +544,132 @@ quarkus.oidc.credentials.secret={GOOGLE_CLIENT_SECRET} quarkus.oidc.token.issuer=https://accounts.google.com ---- +== Provider Endpoint configuration + +OIDC `web-app` application needs to know OpenId Connect provider's authorization, token, `JsonWebKey` (JWK) set and possibly `UserInfo`, introspection and end session (RP-initiated logout) endpoint addresses. + +By default they are discovered by adding a `/.well-known/openid-configuration` path to the configured `quarkus.oidc.auth-server-url`. + +Alternatively, if the discovery endpoint is not available or you would like to save on the discovery endpoint roundtrip, you can disable the discovery and configure them with relative path values, for example: + +[source, properties] +---- +quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +quarkus.oidc.discovery-enabled=false +# Authorization endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/auth +quarkus.oidc.authorization-path=/protocol/openid-connect/auth +# Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/token +quarkus.oidc.token-path=/protocol/openid-connect/token +# JWK set endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/certs +quarkus.oidc.jwks-path=/protocol/openid-connect/certs +# UserInfo endoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/userinfo +quarkus.oidc.user-info-path=/protocol/openid-connect/userinfo +# Token Introspection endoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/token/introspect +quarkus.oidc.introspection-path=/protocol/openid-connect/token/introspect +# End session endoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/logout +quarkus.oidc.end-session-path=/protocol/openid-connect/logout +---- + == Token Propagation Please see link:security-openid-connect-client#token-propagation[Token Propagation] section about the Authorization Code Flow access token propagation to the downstream services. +[[integration-testing]] +== Testing + +Add the following dependencies to your test project: + +[source,xml] +---- + + io.quarkus + quarkus-test-oidc-server + test + + + net.sourceforge.htmlunit + htmlunit + + + org.eclipse.jetty + * + + + test + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + +---- + +Prepare the REST test endpoints, set `application.properties`, for example: + +[source, properties] +---- +# keycloak.url is set by OidcWiremockTestResource +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc.client-id=quarkus-web-app +quarkus.oidc.credentials.secret=secret +quarkus.oidc.application-type=web-app +---- + +and finally write the test code, for example: + +[source, java] +---- +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.oidc.server.OidcWiremockTestResource; + +@QuarkusTest +@QuarkusTestResource(OidcWiremockTestResource.class) +public class CodeFlowAuthorizationTest { + + @Test + public void testCodeFlow() throws Exception { + try (final WebClient webClient = createWebClient()) { + // the test REST endpoint listens on '/code-flow' + HtmlPage page = webClient.getPage("http://localhost:8081/code-flow"); + + HtmlForm form = page.getFormByName("form"); + // user 'alice' has the 'user' role + form.getInputByName("username").type("alice"); + form.getInputByName("password").type("alice"); + + page = form.getInputByValue("login").click(); + + assertEquals("alice", page.getBody().asText()); + } + } + + private WebClient createWebClient() { + WebClient webClient = new WebClient(); + webClient.setCssErrorHandler(new SilentCssErrorHandler()); + return webClient; + } +} +---- + +`OidcWiremockTestResource` recognizes `alice` and `admin` users. The user `alice` has the `user` role only by default - it can be customized with a `quarkus.test.oidc.token.user-roles` system property. The user `admin` has the `user` and `admin` roles by default - it can be customized with a `quarkus.test.oidc.token.admin-roles` system property. +Additionally, `OidcWiremockTestResource` set token issuer and audience to `https://service.example.com` which can be customized with `quarkus.test.oidc.token.issuer` and `quarkus.test.oidc.token.audience` system properties. + +`OidcWiremockTestResource` will be enhanced going forward to support more complex authorization code flow test scenarios. + == Configuration Reference include::{generated-dir}/config/quarkus-oidc.adoc[opts=optional] diff --git a/docs/src/main/asciidoc/security-openid-connect.adoc b/docs/src/main/asciidoc/security-openid-connect.adoc index 15941f07173b4f..a47bb89e7fdea4 100644 --- a/docs/src/main/asciidoc/security-openid-connect.adoc +++ b/docs/src/main/asciidoc/security-openid-connect.adoc @@ -405,10 +405,133 @@ For example, here is how you can use `keycloak.js` to authenticate the users and ---- +== Provider Endpoint configuration + +OIDC `service` application needs to know OpenId Connect provider's token, `JsonWebKey` (JWK) set and possibly `UserInfo` and introspection endpoint addresses. + +By default they are discovered by adding a `/.well-known/openid-configuration` path to the configured `quarkus.oidc.auth-server-url`. + +Alternatively, if the discovery endpoint is not available or you would like to save on the discovery endpoint roundtrip, you can disable the discovery and configure them with relative path values, for example: + +[source, properties] +---- +quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +quarkus.oidc.discovery-enabled=false +# Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens +quarkus.oidc.token-path=/protocol/openid-connect/tokens +# JWK set endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/certs +quarkus.oidc.jwks-path=/protocol/openid-connect/certs +# UserInfo endoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/userinfo +quarkus.oidc.user-info-path=/protocol/openid-connect/userinfo +# Token Introspection endoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens/introspect +quarkus.oidc.introspection-path=/protocol/openid-connect/tokens/introspect +---- + == Token Propagation Please see link:security-openid-connect-client#token-propagation[Token Propagation] section about the Bearer access token propagation to the downstream services. +[[integration-testing]] +== Testing + +=== Wiremock + +Add the following dependencies to your test project: + +[source,xml] +---- + + io.quarkus + quarkus-test-oidc-server + test + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + +---- + +Prepare the REST test endpoint, set `application.properties`, for example: + +[source, properties] +---- +# keycloak.url is set by OidcWiremockTestResource +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc.client-id=quarkus-service-app +quarkus.oidc.application-type=service +# required to sign the tokens +smallrye.jwt.sign.key.location=privateKey.jwk +---- + +and finally write the test code, for example: + +[source, java] +---- +import static org.hamcrest.Matchers.equalTo; + +import java.util.Arrays; +import java.util.HashSet; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.oidc.server.OidcWiremockTestResource; +import io.restassured.RestAssured; +import io.smallrye.jwt.build.Jwt; + +@QuarkusTest +@QuarkusTestResource(OidcWiremockTestResource.class) +public class BearerTokenAuthorizationTest { + + @Test + public void testBearerToken() { + RestAssured.given().auth().oauth2(getAccessToken("alice", new HashSet<>(Arrays.asList("user")))) + .when().get("/api/users/preferredUserName") + .then() + .statusCode(200) + // the test endpoint returns the name extracted from the injected SecurityIdentity Principal + .body("userName", equalTo("alice")); + } + + private String getAccessToken(String userName, Set groups) { + return Jwt.preferredUserName(userName) + .groups(groups) + .issuer("https://server.example.com") + .audience("https://service.example.com") + .jws() + .keyId("1") + .sign(); + } +} +---- + +Testing your `quarkus-oidc` `service` application with `OidcWiremockTestResource` provides the best coverage as even the communication channel is tested against the Wiremock HTTP stubs. +`OidcWiremockTestResource` will be enhanced going forward to support more complex Bearer token test scenarios. + +=== Local Public Key + +You can also use a local inlined public key for testing your `quarkus-oidc` `service` applications: + +[source,properties] +---- +quarkus.oidc.client-id=test +quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB + +smallrye.jwt.sign.key-location=/privateKey.pem +---- + +copy `privateKey.pem` from the `integration-tests/oidc-tenancy` in the `main` Quarkus repository and use a test code similar to the one in the `Wiremock` section above to generate JWT tokens. You can use your own test keys if preferred. + +This approach provides a more limited coverage compared to the Wiremock approach - for example, the remote communication code is not covered. + == References * https://www.keycloak.org/documentation.html[Keycloak Documentation] diff --git a/docs/src/main/asciidoc/security-testing.adoc b/docs/src/main/asciidoc/security-testing.adoc index ff5e16408d395f..6a8d79e6c4e30d 100644 --- a/docs/src/main/asciidoc/security-testing.adoc +++ b/docs/src/main/asciidoc/security-testing.adoc @@ -96,7 +96,7 @@ for example by setting `quarkus.http.auth.basic=true` or `%test.quarkus.http.aut == Use Wiremock for Integration Testing You can also use Wiremock to mock the authorization OAuth2 and OIDC services: -See link:security-oauth2#integration-testing[OAuth2 Integration testing] for more details. +See link:security-oauth2#integration-testing[OAuth2 Integration testing], link:security-openid-connect#integration-testing[OpenId Connect Bearer Token Integration testing], link:security-openid-connect-web-authentication#integration-testing[OpenId Connect Authorization Code Flow Integration testing] and link:security-jwt#integration-testing[SmallRye JWT Integration testing] for more details. == References diff --git a/docs/src/main/asciidoc/microprofile-fault-tolerance.adoc b/docs/src/main/asciidoc/smallrye-fault-tolerance.adoc similarity index 97% rename from docs/src/main/asciidoc/microprofile-fault-tolerance.adoc rename to docs/src/main/asciidoc/smallrye-fault-tolerance.adoc index 6cb2699413ab66..0549355991f36d 100644 --- a/docs/src/main/asciidoc/microprofile-fault-tolerance.adoc +++ b/docs/src/main/asciidoc/smallrye-fault-tolerance.adoc @@ -3,13 +3,15 @@ This guide is maintained in the main Quarkus repository and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// -= Quarkus - Fault Tolerance += Quarkus - SmallRye Fault Tolerance include::./attributes.adoc[] One of the challenges brought by the distributed nature of microservices is that communication with external systems is inherently unreliable. This increases demand on resiliency of applications. To simplify making more resilient -applications, Quarkus contains an implementation of the MicroProfile Fault Tolerance specification. +applications, Quarkus provides https://github.com/smallrye/smallrye-fault-tolerance/[SmallRye Fault Tolerance] an +implementation of the https://github.com/eclipse/microprofile-fault-tolerance/[MicroProfile Fault Tolerance] +specification. In this guide, we demonstrate usage of MicroProfile Fault Tolerance annotations such as `@Timeout`, `@Fallback`, `@Retry` and `@CircuitBreaker`. @@ -55,8 +57,7 @@ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ cd microprofile-fault-tolerance-quickstart ---- -This command generates a Maven structure, importing the extensions for RESTEasy/JAX-RS -and SmallRye Fault Tolerance, which is an implementation of the MicroProfile Fault Tolerance spec that Quarkus uses. +This command generates a Maven structure, importing the extensions for RESTEasy/JAX-RS and SmallRye Fault Tolerance. If you already have your Quarkus project configured, you can add the `smallrye-fault-tolerance` extension to your project by running the following command in your project base directory: @@ -479,7 +480,7 @@ You can also configure a property for all the annotation via `annotation-name/pr == Conclusion -MicroProfile Fault Tolerance allows to improve resiliency of your application, without having an impact on the complexity +SmallRye Fault Tolerance allows to improve resiliency of your application, without having an impact on the complexity of our business logic. All that is needed to enable the fault tolerance features in Quarkus is: diff --git a/docs/src/main/asciidoc/microprofile-graphql.adoc b/docs/src/main/asciidoc/smallrye-graphql.adoc similarity index 95% rename from docs/src/main/asciidoc/microprofile-graphql.adoc rename to docs/src/main/asciidoc/smallrye-graphql.adoc index 789adcf782269d..aab2ad9ebdcf69 100644 --- a/docs/src/main/asciidoc/microprofile-graphql.adoc +++ b/docs/src/main/asciidoc/smallrye-graphql.adoc @@ -3,12 +3,12 @@ This guide is maintained in the main Quarkus repository and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// -= Quarkus - GraphQL += Quarkus - SmallRye GraphQL include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize the **Eclipse MicroProfile -GraphQL** specification through the SmallRye GraphQL extension. +This guide demonstrates how your Quarkus application can use https://github.com/smallrye/smallrye-graphql/[SmallRye GraphQL], +an implementation of the https://github.com/eclipse/microprofile-graphql/[MicroProfile GraphQL] specification. As the https://www.graphql.org/[GraphQL] specification website states: @@ -75,7 +75,7 @@ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ cd microprofile-graphql-quickstart ---- -This command generates a Maven project, importing the `smallrye-graphql` extension which is an implementation of the MicroProfile GraphQL specification used in Quarkus. +This command generates a Maven project, importing the `smallrye-graphql` extension. If you already have your Quarkus project configured, you can add the `smallrye-graphql` extension to your project by running the following command in your project base directory: @@ -367,6 +367,7 @@ curl http://localhost:8080/graphql/schema.graphql The server will return the complete schema of the GraphQL API. +[[ui]] == GraphiQL UI NOTE: Experimental - not included in the MicroProfile specification @@ -848,9 +849,24 @@ When `SomeBusinessException` occurs, the error output will contain the Error cod <1> The error code +== Additional Notes + +If you are using the `smallrye-graphql` extension and the `micrometer` metrics extension is present and metrics are +enabled, you may encounter a `java.lang.NoClassDefFoundError` as some versions of the `smallrye-graphql` extension +have runtime requirements on the Microprofile Metrics API. Add the following Microprofile Metrics API dependency +to resolve the issue: + +[source,xml] +---- + + org.eclipse.microprofile.metrics + microprofile-metrics-api + +---- + == Conclusion -MicroProfile GraphQL enables clients to retrieve the exact data that is +SmallRye GraphQL enables clients to retrieve the exact data that is required preventing `Over-fetching` and `Under-fetching`. The GraphQL API can be expanded without breaking previous queries enabling easy diff --git a/docs/src/main/asciidoc/microprofile-health.adoc b/docs/src/main/asciidoc/smallrye-health.adoc similarity index 95% rename from docs/src/main/asciidoc/microprofile-health.adoc rename to docs/src/main/asciidoc/smallrye-health.adoc index 52e2075173faf4..20f84ccbd098e1 100644 --- a/docs/src/main/asciidoc/microprofile-health.adoc +++ b/docs/src/main/asciidoc/smallrye-health.adoc @@ -3,14 +3,14 @@ This guide is maintained in the main Quarkus repository and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// -= Quarkus - MicroProfile Health += Quarkus - SmallRye Health include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize the MicroProfile -Health specification through the SmallRye Health extension. +This guide demonstrates how your Quarkus application can use https://github.com/smallrye/smallrye-health/[SmallRye Health] +an implementation of the https://github.com/eclipse/microprofile-health/[MicroProfile Health] specification. -MicroProfile Health allows applications to provide information about their state +SmallRye Health allows applications to provide information about their state to external viewers which is typically useful in cloud environments where automated processes must be able to determine whether the application should be discarded or restarted. @@ -54,8 +54,7 @@ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ cd microprofile-health-quickstart ---- -This command generates a Maven project, importing the `smallrye-health` extension -which is an implementation of the MicroProfile Health specification used in Quarkus. +This command generates a Maven project, importing the `smallrye-health` extension. If you already have your Quarkus project configured, you can add the `smallrye-health` extension to your project by running the following command in your project base directory: @@ -143,7 +142,7 @@ to be a liveness procedure (with `@Liveness` qualifier) the new health check pro is now present in the `checks` array. Congratulations! You've created your first Quarkus health check procedure. Let's -continue by exploring what else can be done with the MicroProfile Health specification. +continue by exploring what else can be done with SmallRye Health. == Adding a readiness health check procedure @@ -268,7 +267,7 @@ because we are running Quarkus in dev mode you can add `database.up=true` in In previous sections, we saw how to create simple health checks with only the minimal attributes, namely, the health check name and its status (UP or DOWN). However, the -MicroProfile specification also provides a way for the applications to supply +MicroProfile Health specification also provides a way for the applications to supply arbitrary data in the form of key-value pairs sent to the consuming end. This can be done by using the `withData(key, value)` method of the health check response builder API. @@ -330,6 +329,7 @@ that will validate each datasources: link:datasource#datasource-health-check[Dat You can disable extension health check via the property `quarkus.health.extensions.enabled` so none will be automatically registered. +[[ui]] == Health UI NOTE: Experimental - not included in the MicroProfile specification @@ -344,12 +344,12 @@ image:health-ui-screenshot01.png[alt=Health UI] == Conclusion -MicroProfile Health provides a way for your application to distribute information +SmallRye Health provides a way for your application to distribute information about its healthiness state to state whether or not it is able to function properly. Liveness checks are utilized to tell whether the application should be restarted and readiness checks are used to tell whether the application is able to process requests. -All that is needed to enable the MicroProfile Health features in Quarkus is: +All that is needed to enable the SmallRye Health features in Quarkus is: * adding the `smallrye-health` Quarkus extension to your project using the `quarkus-maven-plugin`: + diff --git a/docs/src/main/asciidoc/microprofile-metrics.adoc b/docs/src/main/asciidoc/smallrye-metrics.adoc similarity index 94% rename from docs/src/main/asciidoc/microprofile-metrics.adoc rename to docs/src/main/asciidoc/smallrye-metrics.adoc index bc1438a5bff3f9..507e1d78cd39eb 100644 --- a/docs/src/main/asciidoc/microprofile-metrics.adoc +++ b/docs/src/main/asciidoc/smallrye-metrics.adoc @@ -3,14 +3,14 @@ This guide is maintained in the main Quarkus repository and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// -= Quarkus - MicroProfile Metrics += Quarkus - SmallRye Metrics include::./attributes.adoc[] -This guide demonstrates how your Quarkus application can utilize the MicroProfile -Metrics specification through the SmallRye Metrics extension. +This guide demonstrates how your Quarkus application can use https://github.com/smallrye/smallrye-metrics/[SmallRye Metrics], +an implementation of the https://github.com/eclipse/microprofile-metrics/[MicroProfile Metrics] specification. -MicroProfile Metrics allows applications to gather various metrics and statistics that provide +SmallRye Metrics allows applications to gather various metrics and statistics that provide insights into what is happening inside the application. The metrics can be read remotely using JSON format or the OpenMetrics format, so that @@ -22,7 +22,7 @@ exposed by various Quarkus extensions. These are described in the guide for each built-in metrics. IMPORTANT: link:micrometer[Micrometer] is the recommended approach to metrics for Quarkus. -Use the MicroProfile Metrics extension when it's required to retain MicroProfile specification compatibility. +Use the SmallRye Metrics extension when it's required to retain MicroProfile specification compatibility. == Prerequisites @@ -62,7 +62,7 @@ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ cd microprofile-metrics-quickstart ---- -This command generates a Maven project, importing the `smallrye-metrics` extension which is an implementation of the MicroProfile Metrics specification used in Quarkus. +This command generates a Maven project, importing the `smallrye-metrics`. If you already have your Quarkus project configured, you can add the `smallrye-metrics` extension to your project by running the following command in your project base directory: diff --git a/docs/src/main/asciidoc/vault-datasource.adoc b/docs/src/main/asciidoc/vault-datasource.adoc index b353510284700b..e7f13a94adf366 100644 --- a/docs/src/main/asciidoc/vault-datasource.adoc +++ b/docs/src/main/asciidoc/vault-datasource.adoc @@ -119,7 +119,7 @@ Now we are ready to configure Vault and Quarkus to be able to connect to this da == Vault MicroProfile Config Source The simplest approach is to write the database password in the KV secret engine under the path that is -fetched from the Vault MicroProfile Config Source. +fetched from the Vault Config Source. Open a new shell, `docker exec` in the Vault container and set the root token: [source,bash, subs=attributes+] @@ -325,4 +325,4 @@ As you can see 2 users have been created: As expected, users have been created dynamically by Vault, with expiration dates, allowing a rotation to occur, without breaking existing connections, allowing a greater level of security than the traditional -_password_ based approaches. \ No newline at end of file +_password_ based approaches. diff --git a/docs/src/main/asciidoc/vault.adoc b/docs/src/main/asciidoc/vault.adoc index 5c383210e7e01f..fa2e56322d7145 100644 --- a/docs/src/main/asciidoc/vault.adoc +++ b/docs/src/main/asciidoc/vault.adoc @@ -278,7 +278,7 @@ quarkus.vault.authentication.userpass.password=sinclair quarkus.vault.secret-config-kv-path=myapps/vault-quickstart/config ---- -This should mount whatever keys are stored in `secret/myapps/vault-quickstart` as MicroProfile Config properties. +This should mount whatever keys are stored in `secret/myapps/vault-quickstart` as Config properties. Let's verify that by adding a new endpoint in GreetingResource: @@ -325,7 +325,7 @@ You should see: [NOTE] ==== -The MicroProfile _Vault Config Source_ is configured with ordinal `270`, which is higher than the file system +The _Vault Config Source_ is configured with ordinal `270`, which is higher than the file system or jar ordinals, but lower than environment variables. This value can be changed using property `quarkus.vault.config-ordinal`. ==== diff --git a/docs/src/main/asciidoc/writing-extensions.adoc b/docs/src/main/asciidoc/writing-extensions.adoc index 2d79e65bd00082..5e498a859043fb 100644 --- a/docs/src/main/asciidoc/writing-extensions.adoc +++ b/docs/src/main/asciidoc/writing-extensions.adoc @@ -57,7 +57,7 @@ The more is done there, the smaller Quarkus applications using that extension wi Quarkus simplifies the most common usages. This means that its defaults might be different than the library it integrates. -To make the simple experience easiest, unify the configuration in `application.properties` via MicroProfile Config. +To make the simple experience easiest, unify the configuration in `application.properties` via SmallRye Config. Avoid library specific configuration files, or at least make them optional: e.g. `persistence.xml` for Hibernate ORM is optional. Extensions should see the configuration holistically as a Quarkus application instead of focusing on the library experience. @@ -1515,7 +1515,7 @@ In order to provide a health check, you should do the following: - Import the `quarkus-smallrye-health` extension as an **optional** dependency in your runtime module so it will not impact the size of the application if health check is not included. -- Create your health check following the link:microprofile-health[Quarkus - MicroProfile Health] guide. We advise providing only +- Create your health check following the link:smallrye-health[SmallRye Health] guide. We advise providing only readiness check for an extension (liveness check is designed to express the fact that an application is up and needs to be lightweight). - Import the `quarkus-smallrye-health-spi` library in your deployment module. - Add a build step in your deployment module that produces a `HealthBuildItem`. diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DevServicesH2DatasourceTestCase.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DevServicesH2DatasourceTestCase.java new file mode 100644 index 00000000000000..79e7838ad8a8bf --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DevServicesH2DatasourceTestCase.java @@ -0,0 +1,52 @@ +package io.quarkus.agroal.test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Connection; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.agroal.api.AgroalDataSource; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration.ExceptionSorter; +import io.quarkus.test.QuarkusUnitTest; + +public class DevServicesH2DatasourceTestCase { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); + + @Inject + AgroalDataSource dataSource; + + @Test + public void testDatasource() throws Exception { + AgroalConnectionPoolConfiguration configuration = dataSource.getConfiguration().connectionPoolConfiguration(); + assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:h2:")); + assertEquals("sa", configuration.connectionFactoryConfiguration().principal().getName()); + assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(ExceptionSorter.emptyExceptionSorter().getClass()); + + try (Connection connection = dataSource.getConnection()) { + } + } +} diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalConnectionConfigurer.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalConnectionConfigurer.java index 71c4b938aade6e..d052e08ec0412e 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalConnectionConfigurer.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalConnectionConfigurer.java @@ -12,4 +12,9 @@ default void disableSslSupport(String databaseKind, AgroalDataSourceConfiguratio log.warnv("Agroal does not support disabling SSL for database kind: {0}", databaseKind); } + default void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + log.warnv("Agroal does not support detecting if a connection is still usable after an exception for database kind: {0}", + databaseKind); + } + } 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 411f2aed726771..e9bb1fe3a6cf58 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 @@ -39,7 +39,6 @@ import io.quarkus.agroal.runtime.DataSourcesJdbcRuntimeConfig.DataSourceJdbcOuterNamedRuntimeConfig; import io.quarkus.agroal.runtime.JdbcDriver.JdbcDriverLiteral; import io.quarkus.arc.Arc; -import io.quarkus.arc.InstanceHandle; import io.quarkus.credentials.CredentialsProvider; import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -150,9 +149,9 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { } String resolvedDbKind = matchingSupportEntry.resolvedDbKind; - InstanceHandle agroalConnectionConfigurerHandle = Arc.container().instance( - AgroalConnectionConfigurer.class, - new JdbcDriverLiteral(resolvedDbKind)); + AgroalConnectionConfigurer agroalConnectionConfigurer = Arc.container() + .instance(AgroalConnectionConfigurer.class, new JdbcDriverLiteral(resolvedDbKind)) + .orElse(new UnknownDbAgroalConnectionConfigurer()); AgroalDataSourceConfigurationSupplier dataSourceConfiguration = new AgroalDataSourceConfigurationSupplier(); @@ -170,15 +169,11 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { dataSourceJdbcBuildTimeConfig, dataSourceRuntimeConfig, dataSourceJdbcRuntimeConfig, mpMetricsPresent); if (dataSourceSupport.disableSslSupport) { - if (agroalConnectionConfigurerHandle.isAvailable()) { - agroalConnectionConfigurerHandle.get().disableSslSupport(resolvedDbKind, - dataSourceConfiguration); - } else { - log.warnv("Agroal does not support disabling SSL for database kind: {0}", - resolvedDbKind); - } + agroalConnectionConfigurer.disableSslSupport(resolvedDbKind, dataSourceConfiguration); } + agroalConnectionConfigurer.setExceptionSorter(resolvedDbKind, dataSourceConfiguration); + // Explicit reference to bypass reflection need of the ServiceLoader used by AgroalDataSource#from AgroalDataSourceConfiguration agroalConfiguration = dataSourceConfiguration.get(); AgroalDataSource dataSource = new io.agroal.pool.DataSource(agroalConfiguration, diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnconfiguredDataSource.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnconfiguredDataSource.java index 0e30f2a092dffb..828031702bb0a7 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnconfiguredDataSource.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnconfiguredDataSource.java @@ -43,7 +43,7 @@ public void flush(FlushMode mode) { } @Override - public void setPoolInterceptors(Collection interceptors) { + public void setPoolInterceptors(Collection arg0) { //noop } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnknownDbAgroalConnectionConfigurer.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnknownDbAgroalConnectionConfigurer.java new file mode 100644 index 00000000000000..d784e806a892ed --- /dev/null +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/UnknownDbAgroalConnectionConfigurer.java @@ -0,0 +1,4 @@ +package io.quarkus.agroal.runtime; + +public class UnknownDbAgroalConnectionConfigurer implements AgroalConnectionConfigurer { +} diff --git a/extensions/amazon-alexa/deployment/pom.xml b/extensions/amazon-alexa/deployment/pom.xml index 435fe773c1a838..2ca0d8ce156a6f 100644 --- a/extensions/amazon-alexa/deployment/pom.xml +++ b/extensions/amazon-alexa/deployment/pom.xml @@ -19,7 +19,10 @@ io.quarkus quarkus-amazon-alexa - + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-jackson-deployment diff --git a/extensions/amazon-alexa/runtime/pom.xml b/extensions/amazon-alexa/runtime/pom.xml index fb60b57c1bf2c9..1a5ca861dfb41e 100644 --- a/extensions/amazon-alexa/runtime/pom.xml +++ b/extensions/amazon-alexa/runtime/pom.xml @@ -16,6 +16,10 @@ Write Amazon Alexa Skills with AWS Lambda or HTTPS endpoints + + io.quarkus + quarkus-apache-httpclient + com.amazon.alexa ask-sdk diff --git a/extensions/amazon-lambda-http/runtime/pom.xml b/extensions/amazon-lambda-http/runtime/pom.xml index 032f4a25bc399d..9efa5c2aa7f6c8 100644 --- a/extensions/amazon-lambda-http/runtime/pom.xml +++ b/extensions/amazon-lambda-http/runtime/pom.xml @@ -37,6 +37,26 @@ svm provided + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + diff --git a/extensions/amazon-lambda-http/runtime/src/main/java/io/quarkus/amazon/lambda/http/LambdaHttpHandler.java b/extensions/amazon-lambda-http/runtime/src/main/java/io/quarkus/amazon/lambda/http/LambdaHttpHandler.java index fa99269e88ee1f..1fcc653280fa2d 100644 --- a/extensions/amazon-lambda-http/runtime/src/main/java/io/quarkus/amazon/lambda/http/LambdaHttpHandler.java +++ b/extensions/amazon-lambda-http/runtime/src/main/java/io/quarkus/amazon/lambda/http/LambdaHttpHandler.java @@ -1,12 +1,17 @@ package io.quarkus.amazon.lambda.http; +import static java.util.Optional.ofNullable; + import java.io.ByteArrayOutputStream; import java.net.InetSocketAddress; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -92,12 +97,21 @@ public void handleMessage(Object msg) { HttpResponse res = (HttpResponse) msg; responseBuilder.setStatusCode(res.status().code()); - Headers multiValueHeaders = new Headers(); - responseBuilder.setMultiValueHeaders(multiValueHeaders); + final Map headers = new HashMap<>(); + responseBuilder.setHeaders(headers); for (String name : res.headers().names()) { - for (String v : res.headers().getAll(name)) { - multiValueHeaders.add(name, v); + final List allForName = res.headers().getAll(name); + if (allForName == null || allForName.isEmpty()) { + continue; + } + final StringBuilder sb = new StringBuilder(); + for (Iterator valueIterator = allForName.iterator(); valueIterator.hasNext();) { + sb.append(valueIterator.next()); + if (valueIterator.hasNext()) { + sb.append(","); + } } + headers.put(name, sb.toString()); } } if (msg instanceof HttpContent) { @@ -122,7 +136,7 @@ public void handleMessage(Object msg) { } if (msg instanceof LastHttpContent) { if (baos != null) { - if (isBinary(((Headers) responseBuilder.getMultiValueHeaders()).getFirst("Content-Type"))) { + if (isBinary(responseBuilder.getHeaders().get("Content-Type"))) { responseBuilder.setIsBase64Encoded(true); responseBuilder.setBody(Base64.getMimeEncoder().encodeToString(baos.toByteArray())); } else { @@ -150,16 +164,14 @@ public void close() { private APIGatewayV2HTTPResponse nettyDispatch(InetSocketAddress clientAddress, APIGatewayV2HTTPEvent request, Context context) throws Exception { - StringBuilder sb = new StringBuilder(request.getRawPath()); - sb.append('?').append(request.getRawQueryString()); - String path = new StringBuilder(request.getRawPath()).append('?').append(request.getRawQueryString()).toString(); - //log.info("---- Got lambda request: " + path); QuarkusHttpHeaders quarkusHeaders = new QuarkusHttpHeaders(); quarkusHeaders.setContextObject(Context.class, context); quarkusHeaders.setContextObject(APIGatewayV2HTTPEvent.class, request); quarkusHeaders.setContextObject(APIGatewayV2HTTPEvent.RequestContext.class, request.getRequestContext()); DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.valueOf(request.getRequestContext().getHttp().getMethod()), path, quarkusHeaders); + HttpMethod.valueOf(request.getRequestContext().getHttp().getMethod()), ofNullable(request.getRawQueryString()) + .filter(q -> !q.isEmpty()).map(q -> request.getRawPath() + '?' + q).orElse(request.getRawPath()), + quarkusHeaders); if (request.getHeaders() != null) { //apparently this can be null if no headers are sent for (Map.Entry header : request.getHeaders().entrySet()) { if (header.getValue() != null) { diff --git a/extensions/amazon-lambda-http/runtime/src/test/java/io/quarkus/amazon/lambda/http/LambdaHttpHandlerTest.java b/extensions/amazon-lambda-http/runtime/src/test/java/io/quarkus/amazon/lambda/http/LambdaHttpHandlerTest.java new file mode 100644 index 00000000000000..fd6cb445e2775d --- /dev/null +++ b/extensions/amazon-lambda-http/runtime/src/test/java/io/quarkus/amazon/lambda/http/LambdaHttpHandlerTest.java @@ -0,0 +1,114 @@ +package io.quarkus.amazon.lambda.http; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; + +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.quarkus.netty.runtime.virtual.VirtualClientConnection; +import io.quarkus.netty.runtime.virtual.VirtualResponseHandler; +import io.quarkus.runtime.Application; + +public class LambdaHttpHandlerTest { + + private static final long PROCESSING_TIMEOUT = TimeUnit.SECONDS.toMillis(1); + + private static final String PATH = "/test/path"; + private static final String QUERY = "testParam1=testValue1&testParam2=testValue2"; + private static final String HOST_HEADER = "Host"; + private static final String HOST = "localhost"; + private static final String METHOD = "GET"; + + private final Application application = mock(Application.class); + private final APIGatewayV2HTTPEvent request = mock(APIGatewayV2HTTPEvent.class); + private final APIGatewayV2HTTPEvent.RequestContext requestContext = mock(APIGatewayV2HTTPEvent.RequestContext.class); + private final APIGatewayV2HTTPEvent.RequestContext.Http requestContextMethod = mock( + APIGatewayV2HTTPEvent.RequestContext.Http.class); + private final Context context = mock(Context.class); + private final VirtualClientConnection connection = mock(VirtualClientConnection.class); + + @BeforeEach + public void mockSetup() { + when(request.getRawPath()).thenReturn(PATH); + when(request.getRequestContext()).thenReturn(requestContext); + when(requestContext.getHttp()).thenReturn(requestContextMethod); + when(requestContextMethod.getMethod()).thenReturn(METHOD); + when(request.getHeaders()).thenReturn(Collections.singletonMap(HOST_HEADER, HOST)); + } + + @SuppressWarnings({ "rawtypes", "unused" }) + private APIGatewayV2HTTPResponse mockHttpFunction(String query, HttpResponseStatus status) + throws ExecutionException, InterruptedException { + when(request.getRawQueryString()).thenReturn(query); + try (MockedStatic applicationMock = Mockito.mockStatic(Application.class)) { + applicationMock.when(Application::currentApplication).thenReturn(application); + LambdaHttpHandler lambda = new LambdaHttpHandler(); + CompletableFuture requestFuture = CompletableFuture.supplyAsync(() -> { + try (MockedStatic connectionMock = Mockito.mockStatic(VirtualClientConnection.class)) { + connectionMock.when(() -> VirtualClientConnection.connect(any(), any(), any())).thenAnswer(i -> { + VirtualResponseHandler handler = i.getArgument(0); + CompletableFuture responseFuture = CompletableFuture.supplyAsync(() -> { + handler.handleMessage(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status)); + return null; + }); + return connection; + }); + return lambda.handleRequest(request, context); + } + }); + return requestFuture.get(); + } + } + + public static Iterable queries() { + return Arrays.asList(new Object[] { QUERY, PATH + "?" + QUERY }, new Object[] { "", PATH }, + new Object[] { null, PATH }); + } + + @ParameterizedTest + @MethodSource("queries") + public void verifyQueryParametersBypass(String query, String expected) throws ExecutionException, InterruptedException { + mockHttpFunction(query, HttpResponseStatus.OK); + ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(captor.capture()); + DefaultHttpRequest rq = (DefaultHttpRequest) captor.getAllValues().get(0); + assertEquals(expected, rq.uri()); + } + + public static Iterable responses() { + return Arrays.asList(new Object[] { HttpResponseStatus.CREATED }, new Object[] { HttpResponseStatus.OK }, + new Object[] { HttpResponseStatus.BAD_REQUEST }); + } + + @ParameterizedTest + @MethodSource("responses") + public void verifyResponseStatusBypass(final HttpResponseStatus status) throws ExecutionException, InterruptedException { + APIGatewayV2HTTPResponse response = mockHttpFunction(null, status); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(any()); + assertEquals(status.code(), response.getStatusCode()); + } + +} diff --git a/extensions/amazon-lambda-rest/deployment/pom.xml b/extensions/amazon-lambda-rest/deployment/pom.xml index bb4da0e13edbcd..f8352f7db4ef5f 100644 --- a/extensions/amazon-lambda-rest/deployment/pom.xml +++ b/extensions/amazon-lambda-rest/deployment/pom.xml @@ -23,6 +23,10 @@ io.quarkus quarkus-vertx-http-deployment + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-amazon-lambda-deployment diff --git a/extensions/amazon-lambda-rest/runtime/pom.xml b/extensions/amazon-lambda-rest/runtime/pom.xml index a6e0f2a074f042..8be168ad30bc67 100644 --- a/extensions/amazon-lambda-rest/runtime/pom.xml +++ b/extensions/amazon-lambda-rest/runtime/pom.xml @@ -27,6 +27,10 @@ io.quarkus quarkus-core + + io.quarkus + quarkus-apache-httpclient + com.amazonaws.serverless aws-serverless-java-container-core diff --git a/extensions/amazon-lambda-xray/deployment/pom.xml b/extensions/amazon-lambda-xray/deployment/pom.xml index 96bff089902eeb..f35b2095131ad0 100644 --- a/extensions/amazon-lambda-xray/deployment/pom.xml +++ b/extensions/amazon-lambda-xray/deployment/pom.xml @@ -19,6 +19,10 @@ io.quarkus quarkus-amazon-lambda-common-deployment + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-amazon-lambda-xray diff --git a/extensions/amazon-lambda-xray/deployment/src/main/java/io/quarkus/amazon/lambda/xray/XrayBuildStep.java b/extensions/amazon-lambda-xray/deployment/src/main/java/io/quarkus/amazon/lambda/xray/XrayBuildStep.java index 8801cafd17a8ec..45f2894b64ec4f 100644 --- a/extensions/amazon-lambda-xray/deployment/src/main/java/io/quarkus/amazon/lambda/xray/XrayBuildStep.java +++ b/extensions/amazon-lambda-xray/deployment/src/main/java/io/quarkus/amazon/lambda/xray/XrayBuildStep.java @@ -19,6 +19,7 @@ public void process(BuildProducer reflectiveClass, runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("com.amazonaws.xray.interceptors.TracingInterceptor")); runtimeInitialized.produce( new RuntimeInitializedClassBuildItem("com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy")); + runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("com.amazonaws.xray.ThreadLocalStorage")); reflectiveClass.produce(new ReflectiveClassBuildItem( true, true, true, "com.amazonaws.xray.handlers.config.AWSServiceHandlerManifest", @@ -47,7 +48,7 @@ public void process(BuildProducer reflectiveClass, "com/amazonaws/xray/strategy/sampling/DefaultSamplingRules.json", "com/amazonaws/xray/sdk.properties")); - //Register Apache client + //Register Apache client proxyDefinition.produce( new NativeImageProxyDefinitionBuildItem("org.apache.http.conn.HttpClientConnectionManager", "org.apache.http.pool.ConnPoolControl", diff --git a/extensions/amazon-lambda-xray/runtime/pom.xml b/extensions/amazon-lambda-xray/runtime/pom.xml index a219a49bdb9b08..687de88ed830b0 100644 --- a/extensions/amazon-lambda-xray/runtime/pom.xml +++ b/extensions/amazon-lambda-xray/runtime/pom.xml @@ -24,10 +24,18 @@ io.quarkus quarkus-core + + io.quarkus + quarkus-apache-httpclient + com.amazonaws aws-xray-recorder-sdk-aws-sdk-v2 + + org.jboss.logging + commons-logging-jboss-logging + org.graalvm.nativeimage svm diff --git a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java index 0777ce720e8a68..db662c1139144b 100644 --- a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java @@ -72,8 +72,11 @@ public void run() { } } else { Object input = null; - if (running.get() && getInputReader() != null) { - input = getInputReader().readValue(requestConnection.getInputStream()); + if (running.get()) { + LambdaInputReader inputReader = getInputReader(); + if (inputReader != null) { + input = inputReader.readValue(requestConnection.getInputStream()); + } Object output = processRequest(input, createContext(requestConnection)); postResponse(url, output); } 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 caa4652ad141ac..8553d6e8d847e8 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 @@ -12,4 +12,4 @@ metadata: name: "amazon-lambda" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/amazon-services/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonServicesClientsProcessor.java b/extensions/amazon-services/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonServicesClientsProcessor.java index 204f2c50f0a9fc..585b7c4fb5c36b 100644 --- a/extensions/amazon-services/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonServicesClientsProcessor.java +++ b/extensions/amazon-services/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonServicesClientsProcessor.java @@ -19,6 +19,7 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.configuration.ConfigurationError; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; @@ -43,6 +44,13 @@ void awsAppArchiveMarkers(BuildProducer producer) { + // FullJitterBackoffStrategy uses j.u.Ramdom, so needs to be runtime-initialized + producer.produce( + new RuntimeInitializedClassBuildItem("software.amazon.awssdk.core.retry.backoff.FullJitterBackoffStrategy")); + } + @BuildStep void setup(CombinedIndexBuildItem combinedIndexBuildItem, List amazonClients, diff --git a/extensions/amazon-services/dynamodb/deployment/src/main/java/io/quarkus/amazon/dynamodb/deployment/DynamodbProcessor.java b/extensions/amazon-services/dynamodb/deployment/src/main/java/io/quarkus/amazon/dynamodb/deployment/DynamodbProcessor.java index 6a2e0d57d33c03..77f5a558ac678a 100644 --- a/extensions/amazon-services/dynamodb/deployment/src/main/java/io/quarkus/amazon/dynamodb/deployment/DynamodbProcessor.java +++ b/extensions/amazon-services/dynamodb/deployment/src/main/java/io/quarkus/amazon/dynamodb/deployment/DynamodbProcessor.java @@ -31,6 +31,7 @@ import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -68,6 +69,14 @@ AdditionalBeanBuildItem producer() { return AdditionalBeanBuildItem.unremovableOf(DynamodbClientProducer.class); } + @BuildStep + void runtimeInitialize(BuildProducer producer) { + // This class triggers initialization of FullJitterBackoffStragegy so needs to get runtime-initialized + // as well + producer.produce( + new RuntimeInitializedClassBuildItem("software.amazon.awssdk.services.dynamodb.DynamoDbRetryPolicy")); + } + @BuildStep void setup(BeanRegistrationPhaseBuildItem beanRegistrationPhase, BuildProducer extensionSslNativeSupport, diff --git a/extensions/apache-httpclient/deployment/pom.xml b/extensions/apache-httpclient/deployment/pom.xml new file mode 100644 index 00000000000000..ffdffa75ba9620 --- /dev/null +++ b/extensions/apache-httpclient/deployment/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + io.quarkus + quarkus-apache-httpclient-parent + 999-SNAPSHOT + + quarkus-apache-httpclient-deployment + Quarkus - Apache HttpClient - Deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-apache-httpclient + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/apache-httpclient/deployment/src/main/java/io/quarkus/apache/http/client/deployment/ApacheHttpClientProcessor.java b/extensions/apache-httpclient/deployment/src/main/java/io/quarkus/apache/http/client/deployment/ApacheHttpClientProcessor.java new file mode 100644 index 00000000000000..d0ddc2a9aa3f41 --- /dev/null +++ b/extensions/apache-httpclient/deployment/src/main/java/io/quarkus/apache/http/client/deployment/ApacheHttpClientProcessor.java @@ -0,0 +1,13 @@ +package io.quarkus.apache.http.client.deployment; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; + +class ApacheHttpClientProcessor { + + @BuildStep + void runtimeInitializedClasses(BuildProducer runtimeInitializedClasses) { + runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("org.apache.http.impl.auth.NTLMEngineImpl")); + } +} diff --git a/extensions/apache-httpclient/pom.xml b/extensions/apache-httpclient/pom.xml new file mode 100644 index 00000000000000..5dc76a69701079 --- /dev/null +++ b/extensions/apache-httpclient/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + io.quarkus + quarkus-extensions-parent + 999-SNAPSHOT + + quarkus-apache-httpclient-parent + pom + Quarkus - Apache HttpClient - Parent + + deployment + runtime + + diff --git a/extensions/apache-httpclient/runtime/pom.xml b/extensions/apache-httpclient/runtime/pom.xml new file mode 100644 index 00000000000000..bd3f78bda686ed --- /dev/null +++ b/extensions/apache-httpclient/runtime/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + io.quarkus + quarkus-apache-httpclient-parent + 999-SNAPSHOT + + quarkus-apache-httpclient + Quarkus - Apache HttpClient - Runtime + Connect to HTTP resources using the Apache HttpClient + + + io.quarkus + quarkus-arc + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.jboss.logging + commons-logging-jboss-logging + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/apache-httpclient/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/apache-httpclient/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..b2c4d4a3c58222 --- /dev/null +++ b/extensions/apache-httpclient/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,10 @@ +--- +name: Apache HttpClient +metadata: + keywords: + - "http" + - "client" + categories: + - "web" + status: "stable" + unlisted: true diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index dad024319f59fd..9ae5bc15f58762 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -360,12 +360,12 @@ public Integer compute(AnnotationTarget target, Collection stere // PHASE 2 - register all beans @BuildStep public BeanRegistrationPhaseBuildItem registerBeans(ContextRegistrationPhaseBuildItem contextRegistrationPhase, - List contextConfigurators, + List contextConfigurationRegistry, BuildProducer interceptorResolver, BuildProducer beanDiscoveryFinished, BuildProducer transformedAnnotations) { - for (ContextConfiguratorBuildItem contextConfigurator : contextConfigurators) { + for (ContextConfiguratorBuildItem contextConfigurator : contextConfigurationRegistry) { for (ContextConfigurator value : contextConfigurator.getValues()) { // Just make sure the configurator is processed value.done(); @@ -385,9 +385,9 @@ public BeanRegistrationPhaseBuildItem registerBeans(ContextRegistrationPhaseBuil // PHASE 3 - register synthetic observers @BuildStep public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistrationPhaseBuildItem beanRegistrationPhase, - List beanConfigurators) { + List beanConfigurationRegistry) { - for (BeanConfiguratorBuildItem configurator : beanConfigurators) { + for (BeanConfiguratorBuildItem configurator : beanConfigurationRegistry) { // Just make sure the configurator is processed configurator.getValues().forEach(BeanConfigurator::done); } @@ -401,12 +401,12 @@ public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistr // PHASE 4 - initialize and validate the bean deployment @BuildStep public ValidationPhaseBuildItem validate(ObserverRegistrationPhaseBuildItem observerRegistrationPhase, - List observerConfigurators, + List observerConfigurationRegistry, List unremovableBeans, BuildProducer bytecodeTransformer, BuildProducer synthesisFinished) { - for (ObserverConfiguratorBuildItem configurator : observerConfigurators) { + for (ObserverConfiguratorBuildItem configurator : observerConfigurationRegistry) { // Just make sure the configurator is processed configurator.getValues().forEach(ObserverConfigurator::done); } 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 75847771074b40..2bb662fd5a7b04 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,10 +1,6 @@ package io.quarkus.arc.deployment; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import org.jboss.jandex.AnnotationInstance; @@ -25,6 +21,7 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; +import io.quarkus.deployment.builditem.ExcludeDependencyBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.index.IndexDependencyConfig; @@ -38,12 +35,12 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil List additionalBeanDefiningAnnotations, List additionalBeans, List generatedBeans, LiveReloadBuildItem liveReloadBuildItem, BuildProducer generatedClass, - CustomScopeAnnotationsBuildItem customScopes) + CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems) throws Exception { // First build an index from application archives IndexView applicationIndex = buildApplicationIndex(config, applicationArchivesBuildItem, - additionalBeanDefiningAnnotations, customScopes); + additionalBeanDefiningAnnotations, customScopes, excludeDependencyBuildItems); // Then build additional index for beans added by extensions Indexer additionalBeanIndexer = new Indexer(); @@ -84,7 +81,7 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem, List additionalBeanDefiningAnnotations, - CustomScopeAnnotationsBuildItem customScopes) { + CustomScopeAnnotationsBuildItem customScopes, List excludeDependencyBuildItems) { Set archives = applicationArchivesBuildItem.getAllApplicationArchives(); @@ -115,7 +112,7 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui List indexes = new ArrayList<>(); for (ApplicationArchive archive : applicationArchivesBuildItem.getApplicationArchives()) { - if (isApplicationArchiveExcluded(config, archive)) { + if (isApplicationArchiveExcluded(config, excludeDependencyBuildItems, archive)) { continue; } IndexView index = archive.getIndex(); @@ -130,20 +127,39 @@ && containsBeanDefiningAnnotation(index, beanDefiningAnnotations))) { return CompositeIndex.create(indexes); } - private boolean isApplicationArchiveExcluded(ArcConfig config, ApplicationArchive archive) { + private boolean isApplicationArchiveExcluded(ArcConfig config, List excludeDependencyBuildItems, + ApplicationArchive archive) { if (archive.getArtifactKey() != null) { AppArtifactKey key = archive.getArtifactKey(); for (IndexDependencyConfig excludeDependency : config.excludeDependency.values()) { - if (Objects.equal(key.getArtifactId(), excludeDependency.artifactId) - && Objects.equal(key.getGroupId(), excludeDependency.groupId)) { - if (excludeDependency.classifier.isPresent()) { - return Objects.equal(key.getClassifier(), excludeDependency.classifier.get()); - } else { - return true; - } + if (archiveMatches(key, excludeDependency.groupId, excludeDependency.artifactId, + excludeDependency.classifier)) { + return true; + } + } + + for (ExcludeDependencyBuildItem excludeDependencyBuildItem : excludeDependencyBuildItems) { + if (archiveMatches(key, excludeDependencyBuildItem.getGroupId(), excludeDependencyBuildItem.getArtifactId(), + excludeDependencyBuildItem.getClassifier())) { + return true; } } } + + return false; + } + + public static boolean archiveMatches(AppArtifactKey key, String groupId, String artifactId, Optional classifier) { + + if (Objects.equal(key.getArtifactId(), artifactId) + && Objects.equal(key.getGroupId(), groupId)) { + if (classifier.isPresent() && Objects.equal(key.getClassifier(), classifier.get())) { + return true; + } else if (!classifier.isPresent() && "".equals(key.getClassifier())) { + return true; + } + } + return false; } 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 dc5d10e7a70d26..e503f4c061fd0f 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 @@ -2,17 +2,15 @@ import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import static io.smallrye.config.ConfigMappings.ConfigMappingWithPrefix.configMappingWithPrefix; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import java.util.ArrayList; 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; import javax.enterprise.context.Dependent; @@ -35,6 +33,7 @@ import io.quarkus.arc.runtime.ConfigBeanCreator; import io.quarkus.arc.runtime.ConfigMappingCreator; import io.quarkus.arc.runtime.ConfigRecorder; +import io.quarkus.arc.runtime.ConfigRecorder.ConfigValidationMetadata; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; @@ -57,16 +56,17 @@ */ public class ConfigBuildStep { - private static final DotName CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); + private static final DotName MP_CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); + private static final DotName CONFIG_MAPPING_NAME = DotName.createSimple(ConfigMapping.class.getName()); private static final DotName SET_NAME = DotName.createSimple(Set.class.getName()); private static final DotName LIST_NAME = DotName.createSimple(List.class.getName()); - + private static final DotName SUPPLIER_NAME = DotName.createSimple(Supplier.class.getName()); private static final DotName CONFIG_VALUE_NAME = DotName.createSimple(io.smallrye.config.ConfigValue.class.getName()); @BuildStep - AdditionalBeanBuildItem bean() { - return new AdditionalBeanBuildItem(ConfigProducer.class); + void additionalBeans(BuildProducer additionalBeans) { + additionalBeans.produce(new AdditionalBeanBuildItem(ConfigProducer.class)); } @BuildStep @@ -83,7 +83,7 @@ void analyzeConfigPropertyInjectionPoints(BeanDiscoveryFinishedBuildItem beanDis continue; } - AnnotationInstance configProperty = injectionPoint.getRequiredQualifier(CONFIG_PROPERTY_NAME); + AnnotationInstance configProperty = injectionPoint.getRequiredQualifier(MP_CONFIG_PROPERTY_NAME); if (configProperty != null) { AnnotationValue nameValue = configProperty.value("name"); AnnotationValue defaultValue = configProperty.value("defaultValue"); @@ -113,16 +113,21 @@ void analyzeConfigPropertyInjectionPoints(BeanDiscoveryFinishedBuildItem beanDis if (DotNames.OPTIONAL.equals(requiredType.name()) || DotNames.OPTIONAL_INT.equals(requiredType.name()) || DotNames.OPTIONAL_LONG.equals(requiredType.name()) - || DotNames.OPTIONAL_DOUBLE.equals(requiredType.name())) { - // Never validate Optional values + || DotNames.OPTIONAL_DOUBLE.equals(requiredType.name()) + || DotNames.PROVIDER.equals(requiredType.name()) + || SUPPLIER_NAME.equals(requiredType.name()) + || CONFIG_VALUE_NAME.equals(requiredType.name())) { + // Never validate container objects continue; } - if (defaultValue != null && !ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValue.asString())) { - // No need to validate properties with default values - continue; + + String propertyDefaultValue = null; + if (defaultValue != null && (ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValue.asString()) + || !"".equals(defaultValue.asString()))) { + propertyDefaultValue = defaultValue.asString(); } - configProperties.produce(new ConfigPropertyBuildItem(propertyName, requiredType)); + configProperties.produce(new ConfigPropertyBuildItem(propertyName, requiredType, propertyDefaultValue)); } } @@ -137,7 +142,7 @@ void analyzeConfigPropertyInjectionPoints(BeanDiscoveryFinishedBuildItem beanDis .creator(ConfigBeanCreator.class) .providerType(type) .types(type) - .addQualifier(CONFIG_PROPERTY_NAME) + .addQualifier(MP_CONFIG_PROPERTY_NAME) .param("requiredType", type.name().toString()).done()); } } @@ -157,10 +162,14 @@ void validateConfigProperties(ConfigRecorder recorder, List> propNamesToClasses = configProperties.stream().collect( - groupingBy(ConfigPropertyBuildItem::getPropertyName, - mapping(c -> c.getPropertyType().name().toString(), toSet()))); - recorder.validateConfigProperties(propNamesToClasses); + Set propertiesToValidate = new HashSet<>(); + for (ConfigPropertyBuildItem configProperty : configProperties) { + propertiesToValidate.add(new ConfigValidationMetadata(configProperty.getPropertyName(), + configProperty.getPropertyType().name().toString(), + configProperty.getDefaultValue())); + } + + recorder.validateConfigProperties(propertiesToValidate); } @BuildStep @@ -174,7 +183,7 @@ void registerConfigRootsAsBeans(ConfigurationBuildItem configItem, BuildProducer // e.g. return Config.ApplicationConfig ResultHandle configRoot = mc.readStaticField(rootDefinition.getDescriptor()); // BUILD_AND_RUN_TIME_FIXED roots are always set before the container is started (in the static initializer of the generated Config class) - // However, RUN_TIME roots may be not be set when the bean instance is created + // However, RUN_TIME roots may be not be set when the bean instance is created mc.ifNull(configRoot).trueBranch().throwException(CreationException.class, String.format("Config root [%s] with config phase [%s] not initialized yet.", configRootClass.getName(), rootDefinition.getConfigPhase().name())); @@ -191,16 +200,17 @@ void generateConfigMappings( BuildProducer generatedClasses, BuildProducer reflectiveClasses, BuildProducer configMappings, - BuildProducer beanConfigurators) { + BuildProducer beanConfigurationRegistry) { + + List mappingAnnotations = new ArrayList<>(); + mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME)); - for (AnnotationInstance instance : combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME)) { + for (AnnotationInstance instance : mappingAnnotations) { AnnotationTarget target = instance.target(); - if (!target.kind().equals(AnnotationTarget.Kind.CLASS)) { - continue; - } + AnnotationValue annotationPrefix = instance.value("prefix"); - Class type = toClass(target.asClass()); - String prefix = Optional.ofNullable(instance.value("prefix")).map(AnnotationValue::asString).orElse(""); + Class type = toClass(target.asClass().name()); + String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse(""); List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(type); List mappingsInfo = new ArrayList<>(); @@ -234,7 +244,7 @@ void generateConfigMappings( configMappings.produce(new ConfigMappingBuildItem(type, prefix)); - beanConfigurators.produce(new BeanConfiguratorBuildItem( + beanConfigurationRegistry.produce(new BeanConfiguratorBuildItem( beanRegistrationPhase.getContext() .configure(type) .types(type) @@ -260,13 +270,12 @@ void registerConfigMappings( .collect(toSet())); } - private static Class toClass(ClassInfo classInfo) { - String className = classInfo.name().toString(); + private static Class toClass(DotName dotName) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { - return classLoader.loadClass(className); + return classLoader.loadClass(dotName.toString()); } catch (ClassNotFoundException e) { - throw new IllegalStateException("The class (" + className + ") cannot be created during deployment.", e); + throw new IllegalStateException("The class (" + dotName.toString() + ") cannot be created during deployment.", e); } } @@ -302,6 +311,7 @@ public static boolean isHandledByProducers(Type type) { DotNames.SHORT.equals(type.name()) || DotNames.BYTE.equals(type.name()) || DotNames.CHARACTER.equals(type.name()) || + SUPPLIER_NAME.equals(type.name()) || CONFIG_VALUE_NAME.equals(type.name()); } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java index 14003a02df1c51..ad5fef88ab6cf6 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java @@ -8,14 +8,14 @@ * Represents a mandatory config property that needs to be validated at runtime. */ public final class ConfigPropertyBuildItem extends MultiBuildItem { - private final String propertyName; - private final Type propertyType; + private final String defaultValue; - public ConfigPropertyBuildItem(String propertyName, Type propertyType) { + public ConfigPropertyBuildItem(final String propertyName, final Type propertyType, final String defaultValue) { this.propertyName = propertyName; this.propertyType = propertyType; + this.defaultValue = defaultValue; } public String getPropertyName() { @@ -26,4 +26,7 @@ public Type getPropertyType() { return propertyType; } + public String getDefaultValue() { + return defaultValue; + } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java index 84212e48284a30..1263eb50bf3643 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java @@ -414,7 +414,8 @@ private ResultHandle populateConfigObject(ClassLoader classLoader, ClassInfo con for (ConfigPropertyBuildItemCandidate candidate : configPropertyBuildItemCandidates) { configProperties - .produce(new ConfigPropertyBuildItem(candidate.getConfigPropertyName(), candidate.getConfigPropertyType())); + .produce(new ConfigPropertyBuildItem(candidate.getConfigPropertyName(), candidate.getConfigPropertyType(), + null)); } return configObject; diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java index 0efa06befc810f..b609af73d8949e 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java @@ -275,7 +275,7 @@ private String generateImplementationForInterfaceConfigPropertiesRec(ClassInfo o methodCreator.returnValue(value); if (defaultValueStr == null || ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValueStr)) { configProperties - .produce(new ConfigPropertyBuildItem(fullConfigName, returnType)); + .produce(new ConfigPropertyBuildItem(fullConfigName, returnType, defaultValueStr)); } } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/YamlListObjectHandler.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/YamlListObjectHandler.java index 8bfc071cc6f923..87b0c87b8cc3c3 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/YamlListObjectHandler.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/YamlListObjectHandler.java @@ -157,7 +157,7 @@ private void validateClass(ClassInfo classInfo, Member member) { } if (!classInfo.hasNoArgsConstructor()) { throw new IllegalArgumentException( - String.format("Class '%s' which is used as %s in class '%s' must be have a no-args constructor", classInfo, + String.format("Class '%s' which is used as %s in class '%s' must have a no-args constructor", classInfo, member.phraseUsage(), member.declaringClass().name().toString())); } if (!Modifier.isPublic(classInfo.flags())) { diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValuesTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValues.java similarity index 63% rename from extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValuesTest.java rename to extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValues.java index 5ff2078ff58d5d..4380489a09b03a 100644 --- a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValuesTest.java +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigDefaultValues.java @@ -15,17 +15,12 @@ import io.quarkus.test.QuarkusUnitTest; -public class ConfigDefaultValuesTest { +public class ConfigDefaultValues { @RegisterExtension static final QuarkusUnitTest TEST = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addAsResource(new StringAsset( - "config_ordinal=1000\n" + - "my.prop=1234\n" + - "%prod.my.prop=1234\n" + - "%dev.my.prop=5678\n" + - "%test.my.prop=1234"), - "application.properties")); + .addAsResource(new StringAsset("config_ordinal=1000\n" + + "my.prop=1234\n"), "application.properties")); @Inject Config config; @@ -44,17 +39,6 @@ void configDefaultValues() { assertEquals("1234", applicationProperties.getValue("my.prop")); } - @Test - void profileDefaultValues() { - ConfigSource defaultValues = getConfigSourceByName("PropertiesConfigSource[source=Specified default values]"); - assertNotNull(defaultValues); - assertEquals("1234", defaultValues.getValue("my.prop")); - assertEquals("1234", defaultValues.getValue("%prod.my.prop")); - assertEquals("5678", defaultValues.getValue("%dev.my.prop")); - assertEquals("1234", defaultValues.getValue("%test.my.prop")); - assertEquals("1234", config.getValue("my.prop", String.class)); - } - private ConfigSource getConfigSourceByName(String name) { for (ConfigSource configSource : config.getConfigSources()) { if (configSource.getName().contains(name)) { diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/IndexedPropertiesInjectionTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/IndexedPropertiesInjectionTest.java new file mode 100644 index 00000000000000..144368f835a6ec --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/IndexedPropertiesInjectionTest.java @@ -0,0 +1,225 @@ +package io.quarkus.arc.test.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class IndexedPropertiesInjectionTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(IndexedBean.class) + .addAsServiceProvider(Converter.class, ConvertedValueConverter.class) + .addAsResource(new StringAsset( + "my.prop=1234\n" + + "server.hosts[0]=localhost\n" + + "server.hosts[1]=config\n" + + "indexed.converted[0]=in\n" + + "indexed.override.defaults[0]=e\n" + + "indexed.override.defaults[1]=f\n" + + "indexed.comma=a,b,c\n" + + "indexed.comma[0]=a\n" + + "indexed.comma[1]=b\n" + + "optionals.indexed[0]=a\n" + + "optionals.indexed[1]=b\n" + + "supplier.indexed[0]=a\n" + + "supplier.indexed[1]=b\n"), + "application.properties")) + .setExpectedException(DeploymentException.class); + + @Inject + IndexedBean indexedBean; + + @Test + void indexed() { + assertEquals("localhost", indexedBean.getHost0()); + assertEquals("config", indexedBean.getHost1()); + assertEquals(Stream.of("localhost", "config").collect(Collectors.toList()), indexedBean.getHosts()); + assertEquals(Stream.of("localhost", "config").collect(Collectors.toSet()), indexedBean.getHostsSet()); + assertEquals(Stream.of(new ConvertedValue("out")).collect(Collectors.toList()), indexedBean.getConverted()); + assertEquals(Stream.of("a", "b", "c").collect(Collectors.toList()), indexedBean.getDefaults()); + assertEquals(Stream.of("e", "f").collect(Collectors.toList()), indexedBean.getOverrideDefaults()); + assertEquals(Stream.of("a", "b", "c").collect(Collectors.toList()), indexedBean.getComma()); + } + + @Test + void optionals() { + assertFalse(indexedBean.getOptionalEmpty().isPresent()); + assertTrue(indexedBean.getOptionalDefaults().isPresent()); + assertEquals(Stream.of("a", "b", "c").collect(Collectors.toList()), indexedBean.getOptionalDefaults().get()); + assertTrue(indexedBean.getOptionalIndexed().isPresent()); + assertEquals(Stream.of("a", "b").collect(Collectors.toList()), indexedBean.getOptionalIndexed().get()); + } + + @Test + void suppliers() { + assertThrows(NoSuchElementException.class, () -> indexedBean.getSupplierEmpty().get()); + assertEquals(Stream.of("a", "b", "c").collect(Collectors.toList()), indexedBean.getSupplierDefaults().get()); + assertEquals(Stream.of("a", "b").collect(Collectors.toList()), indexedBean.getSupplierIndexed().get()); + } + + @ApplicationScoped + static class IndexedBean { + @Inject + @ConfigProperty(name = "server.hosts[0]") + String host0; + @Inject + @ConfigProperty(name = "server.hosts[1]") + String host1; + @Inject + @ConfigProperty(name = "server.hosts") + List hosts; + @Inject + @ConfigProperty(name = "server.hosts") + Set hostsSet; + @Inject + @ConfigProperty(name = "indexed.converted") + List converted; + @Inject + @ConfigProperty(name = "indexed.defaults", defaultValue = "a,b,c") + List defaults; + @Inject + @ConfigProperty(name = "indexed.override.defaults", defaultValue = "a,b,c") + List overrideDefaults; + @Inject + @ConfigProperty(name = "indexed.comma") + List comma; + @Inject + @ConfigProperty(name = "optionals.empty") + Optional> optionalEmpty; + @Inject + @ConfigProperty(name = "optionals.defaults", defaultValue = "a,b,c") + Optional> optionalDefaults; + @Inject + @ConfigProperty(name = "optionals.indexed") + Optional> optionalIndexed; + @Inject + @ConfigProperty(name = "supplier.empty") + Supplier> supplierEmpty; + @Inject + @ConfigProperty(name = "supplier.defaults", defaultValue = "a,b,c") + Supplier> supplierDefaults; + @Inject + @ConfigProperty(name = "supplier.indexed") + Supplier> supplierIndexed; + + public String getHost0() { + return host0; + } + + public String getHost1() { + return host1; + } + + public List getHosts() { + return hosts; + } + + public Set getHostsSet() { + return hostsSet; + } + + public List getConverted() { + return converted; + } + + public List getDefaults() { + return defaults; + } + + public List getOverrideDefaults() { + return overrideDefaults; + } + + public List getComma() { + return comma; + } + + public Optional> getOptionalEmpty() { + return optionalEmpty; + } + + public Optional> getOptionalDefaults() { + return optionalDefaults; + } + + public Optional> getOptionalIndexed() { + return optionalIndexed; + } + + public Supplier> getSupplierEmpty() { + return supplierEmpty; + } + + public Supplier> getSupplierDefaults() { + return supplierDefaults; + } + + public Supplier> getSupplierIndexed() { + return supplierIndexed; + } + } + + public static class ConvertedValue { + private final String value; + + public ConvertedValue(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ConvertedValue that = (ConvertedValue) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + } + + public static class ConvertedValueConverter implements Converter { + @Override + public ConvertedValue convert(final String value) { + if (value == null || value.isEmpty()) { + return null; + } + return new ConvertedValue("out"); + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java new file mode 100644 index 00000000000000..afd8ecf2f1128f --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java @@ -0,0 +1,49 @@ +package io.quarkus.arc.test.config; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class NullConverterTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(NullConverterBean.class) + .addAsServiceProvider(Converter.class, CustomTypeConverter.class) + .addAsResource(new StringAsset("my.prop=1234\n"), "application.properties")) + .setExpectedException(DeploymentException.class); + + @Test + void nullProperty() { + + } + + @ApplicationScoped + static class NullConverterBean { + @Inject + @ConfigProperty(name = "my.prop", defaultValue = "1234") + CustomType customType; + } + + static class CustomType { + + } + + public static class CustomTypeConverter implements Converter { + @Override + public CustomTypeConverter convert(final String value) + throws IllegalArgumentException, NullPointerException { + return null; + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/deployment/TestArchiveMatching.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/deployment/TestArchiveMatching.java new file mode 100644 index 00000000000000..6375e4148d0815 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/deployment/TestArchiveMatching.java @@ -0,0 +1,33 @@ +package io.quarkus.arc.test.deployment; + +import static io.quarkus.arc.deployment.BeanArchiveProcessor.archiveMatches; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import io.quarkus.bootstrap.model.AppArtifactKey; + +public class TestArchiveMatching { + + public static final String GROUP_ID = "io.quarkus"; + public static final String ARTIFACT_ID = "test"; + public static final String CLASSIFIER = "classifier"; + + @Test + public void testMatch() { + + AppArtifactKey key = AppArtifactKey.fromString(GROUP_ID + ":" + ARTIFACT_ID); + AppArtifactKey keyWithClassifier = AppArtifactKey.fromString(GROUP_ID + ":" + ARTIFACT_ID + ":" + CLASSIFIER); + + assertTrue(archiveMatches(key, GROUP_ID, ARTIFACT_ID, Optional.empty())); + assertFalse(archiveMatches(key, GROUP_ID, ARTIFACT_ID, Optional.of(CLASSIFIER))); + assertFalse(archiveMatches(key, GROUP_ID, "test1", Optional.empty())); + + assertTrue(archiveMatches(keyWithClassifier, GROUP_ID, ARTIFACT_ID, Optional.of(CLASSIFIER))); + assertFalse(archiveMatches(keyWithClassifier, GROUP_ID, "test1", Optional.of(CLASSIFIER))); + assertFalse(archiveMatches(keyWithClassifier, GROUP_ID, ARTIFACT_ID, Optional.empty())); + } + +} diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java index 3f652d1dade3fd..88092c81b6bb84 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java @@ -1,22 +1,18 @@ package io.quarkus.arc.runtime; -import java.lang.annotation.Annotation; import java.util.Map; -import java.util.Optional; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.DeploymentException; import javax.enterprise.inject.spi.InjectionPoint; -import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.inject.ConfigProperty; import io.quarkus.arc.BeanCreator; import io.quarkus.arc.impl.InjectionPointProvider; -import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.inject.ConfigProducerUtil; public class ConfigBeanCreator implements BeanCreator { - @Override public Object create(CreationalContext creationalContext, Map params) { String requiredType = params.get("requiredType").toString(); @@ -24,9 +20,9 @@ public Object create(CreationalContext creationalContext, Map clazz; + try { - clazz = Class.forName(requiredType, true, cl); + Class.forName(requiredType, true, cl); } catch (ClassNotFoundException e) { throw new IllegalStateException("Cannot load required type: " + requiredType); } @@ -36,38 +32,10 @@ public Object create(CreationalContext creationalContext, Map value = config.getOptionalValue(key, clazz); - if (value.isPresent()) { - return value.get(); - } else { - return ((SmallRyeConfig) config).convert(defaultValue, clazz); - } - } - } - - private Config getConfig() { - return ConfigProvider.getConfig(); - } - - private ConfigProperty getConfigProperty(InjectionPoint injectionPoint) { - for (Annotation qualifier : injectionPoint.getQualifiers()) { - if (qualifier.annotationType().equals(ConfigProperty.class)) { - return (ConfigProperty) qualifier; - } + try { + return ConfigProducerUtil.getValue(injectionPoint, ConfigProvider.getConfig()); + } catch (Exception e) { + throw new DeploymentException(e); } - return null; } - } diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java index 59bce34c1a713e..68350c3b60d3f4 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java @@ -1,7 +1,6 @@ package io.quarkus.arc.runtime; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import javax.enterprise.inject.spi.DeploymentException; @@ -11,7 +10,9 @@ import io.quarkus.runtime.annotations.Recorder; import io.smallrye.config.ConfigMappings; +import io.smallrye.config.ConfigValidationException; import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.inject.ConfigProducerUtil; /** * @author Martin Kouba @@ -19,37 +20,36 @@ @Recorder public class ConfigRecorder { - public void validateConfigProperties(Map> properties) { + public void validateConfigProperties(Set properties) { Config config = ConfigProvider.getConfig(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ConfigRecorder.class.getClassLoader(); } - for (Entry> entry : properties.entrySet()) { - Set propertyTypes = entry.getValue(); - for (String propertyType : propertyTypes) { - Class propertyClass = load(propertyType, cl); - // For parameterized types and arrays, we only check if the property config exists without trying to convert it - if (propertyClass.isArray() || propertyClass.getTypeParameters().length > 0) { - propertyClass = String.class; - } - try { - if (!config.getOptionalValue(entry.getKey(), propertyClass).isPresent()) { - throw new DeploymentException( - "No config value of type " + entry.getValue() + " exists for: " + entry.getKey()); - } - } catch (IllegalArgumentException e) { - throw new DeploymentException( - "Failed to load config value of type " + entry.getValue() + " for: " + entry.getKey(), e); - } + + for (ConfigValidationMetadata property : properties) { + Class propertyClass = load(property.getType(), cl); + // For parameterized types and arrays, we only check if the property config exists without trying to convert it + if (propertyClass.isArray() || propertyClass.getTypeParameters().length > 0) { + propertyClass = String.class; + } + + try { + ConfigProducerUtil.getValue(property.getName(), propertyClass, property.getDefaultValue(), config); + } catch (Exception e) { + throw new DeploymentException( + "Failed to load config value of type " + propertyClass + " for: " + property.getName(), e); } } } - public void registerConfigMappings(final Set configMappingsWithPrefix) - throws Exception { - SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); - ConfigMappings.registerConfigMappings(config, configMappingsWithPrefix); + public void registerConfigMappings(final Set configMappingsWithPrefix) { + try { + SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); + ConfigMappings.registerConfigMappings(config, configMappingsWithPrefix); + } catch (ConfigValidationException e) { + throw new DeploymentException(e.getMessage(), e); + } } private Class load(String className, ClassLoader cl) { @@ -81,4 +81,61 @@ private Class load(String className, ClassLoader cl) { } } + public static class ConfigValidationMetadata { + private String name; + private String type; + private String defaultValue; + + public ConfigValidationMetadata() { + } + + public ConfigValidationMetadata(final String name, final String type, final String defaultValue) { + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(final String defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ConfigValidationMetadata that = (ConfigValidationMetadata) o; + return name.equals(that.name) && + type.equals(that.type) && + Objects.equals(defaultValue, that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, defaultValue); + } + } } diff --git a/extensions/artemis-core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisCoreProcessor.java b/extensions/artemis-core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisCoreProcessor.java index 97aba3d6076f61..4e027ee66bc037 100644 --- a/extensions/artemis-core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisCoreProcessor.java +++ b/extensions/artemis-core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisCoreProcessor.java @@ -49,7 +49,8 @@ public class ArtemisCoreProcessor { @BuildStep NativeImageConfigBuildItem config() { return NativeImageConfigBuildItem.builder() - .addRuntimeInitializedClass("org.apache.activemq.artemis.api.core.ActiveMQBuffers").build(); + .addRuntimeInitializedClass("org.apache.activemq.artemis.api.core.ActiveMQBuffers") + .addRuntimeInitializedClass("org.apache.activemq.artemis.utils.RandomUtil").build(); } @BuildStep diff --git a/extensions/azure-functions-http/runtime/pom.xml b/extensions/azure-functions-http/runtime/pom.xml index d77049dd8bcca8..f7d176bcf73ca7 100644 --- a/extensions/azure-functions-http/runtime/pom.xml +++ b/extensions/azure-functions-http/runtime/pom.xml @@ -32,6 +32,26 @@ azure-functions-java-library provided + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + diff --git a/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 628632236297d2..e03b7f3c3561e0 100644 --- a/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -12,4 +12,4 @@ metadata: name: "azure-functions-http" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java b/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java new file mode 100644 index 00000000000000..f681a6981510d3 --- /dev/null +++ b/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java @@ -0,0 +1,123 @@ +package io.quarkus.azure.functions.resteasy.runtime; + +import static java.util.Optional.ofNullable; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.microsoft.azure.functions.ExecutionContext; +import com.microsoft.azure.functions.HttpMethod; +import com.microsoft.azure.functions.HttpRequestMessage; +import com.microsoft.azure.functions.HttpResponseMessage; +import com.microsoft.azure.functions.HttpStatus; + +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.quarkus.netty.runtime.virtual.VirtualClientConnection; +import io.quarkus.netty.runtime.virtual.VirtualResponseHandler; +import io.quarkus.runtime.Application; + +@SuppressWarnings("unchecked") +public class FunctionTest { + private static final long PROCESSING_TIMEOUT = TimeUnit.SECONDS.toMillis(1); + + private static final String PATH = "/test/path"; + private static final String QUERY = "testParam1=testValue1&testParam2=testValue2"; + private static final String HOST_HEADER = "Host"; + private static final String HOST = "localhost"; + private static final HttpMethod METHOD = HttpMethod.GET; + + private final Application application = mock(Application.class); + private final HttpRequestMessage> request = mock(HttpRequestMessage.class); + private final ExecutionContext context = mock(ExecutionContext.class); + private final VirtualClientConnection connection = mock(VirtualClientConnection.class); + private final HttpResponseMessage.Builder responseBuilder = mock(HttpResponseMessage.Builder.class); + private final HttpResponseMessage response = mock(HttpResponseMessage.class); + + @BeforeEach + public void mockSetup() { + when(request.getHttpMethod()).thenReturn(METHOD); + when(request.getHeaders()).thenReturn(Collections.singletonMap(HOST_HEADER, HOST)); + when(request.getBody()).thenReturn(Optional.empty()); + when(request.createResponseBuilder(any(HttpStatus.class))).thenReturn(responseBuilder); + when(responseBuilder.build()).thenReturn(response); + } + + @SuppressWarnings({ "rawtypes", "unused" }) + private void mockHttpFunction(String query, HttpResponseStatus status) + throws ExecutionException, InterruptedException, URISyntaxException { + URI requestUri = new URI("http://" + HOST + PATH + ofNullable(query).map(q -> "?" + q).orElse("")); + when(request.getUri()).thenReturn(requestUri); + CompletableFuture requestFuture = CompletableFuture.supplyAsync(() -> { + try (MockedStatic applicationMock = Mockito.mockStatic(Application.class)) { + applicationMock.when(Application::currentApplication).thenReturn(application); + Function function = new Function(); + try (MockedStatic connectionMock = Mockito + .mockStatic(VirtualClientConnection.class)) { + connectionMock.when(() -> VirtualClientConnection.connect(any(), any())).thenAnswer(i -> { + VirtualResponseHandler handler = i.getArgument(0); + CompletableFuture responseFuture = CompletableFuture.supplyAsync(() -> { + handler.handleMessage(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status)); + return null; + }); + return connection; + }); + return function.run(request, context); + } + } + }); + requestFuture.get(); + } + + public static Iterable queries() { + return Arrays.asList(new Object[] { QUERY, PATH + "?" + QUERY }, new Object[] { null, PATH }); + } + + @ParameterizedTest + @MethodSource("queries") + public void verifyQueryParametersBypass(String query, String expected) + throws ExecutionException, InterruptedException, URISyntaxException { + mockHttpFunction(query, HttpResponseStatus.OK); + ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(captor.capture()); + DefaultHttpRequest rq = (DefaultHttpRequest) captor.getAllValues().get(0); + assertEquals(expected, rq.uri()); + } + + public static Iterable responses() { + return Arrays.asList(new Object[] { HttpResponseStatus.CREATED }, new Object[] { HttpResponseStatus.OK }, + new Object[] { HttpResponseStatus.BAD_REQUEST }); + } + + @ParameterizedTest + @MethodSource("responses") + public void verifyResponseStatusBypass(final HttpResponseStatus status) + throws ExecutionException, InterruptedException, URISyntaxException { + mockHttpFunction(null, status); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(any()); + ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(HttpStatus.class); + verify(request, timeout(PROCESSING_TIMEOUT)).createResponseBuilder(statusCaptor.capture()); + assertEquals(status.code(), statusCaptor.getValue().value()); + } +} diff --git a/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 3335e7ff29e838..2ee1bbc6ae8e35 100644 --- a/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -14,4 +14,4 @@ metadata: languages: - "java" - "kotlin" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/container-image/container-image-openshift/deployment/pom.xml b/extensions/container-image/container-image-openshift/deployment/pom.xml index 822a2c7a0cd536..c101061d05f689 100644 --- a/extensions/container-image/container-image-openshift/deployment/pom.xml +++ b/extensions/container-image/container-image-openshift/deployment/pom.xml @@ -16,7 +16,7 @@ io.quarkus - quarkus-kubernetes-client-deployment-internal + quarkus-kubernetes-client-internal-deployment io.quarkus diff --git a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/ApplyDockerfileToBuildConfigDecorator.java b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/ApplyDockerfileToBuildConfigDecorator.java index 475aba86ddd0d5..72a9b96b47a6eb 100644 --- a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/ApplyDockerfileToBuildConfigDecorator.java +++ b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/ApplyDockerfileToBuildConfigDecorator.java @@ -1,14 +1,18 @@ package io.quarkus.container.image.openshift.deployment; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; +import java.util.stream.Stream; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.openshift.api.model.BuildConfigSpecFluent; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.openshift.api.model.BuildConfigSpecFluent; import io.quarkus.deployment.util.FileUtil; public class ApplyDockerfileToBuildConfigDecorator extends NamedResourceDecorator> { @@ -17,9 +21,35 @@ public class ApplyDockerfileToBuildConfigDecorator extends NamedResourceDecorato public ApplyDockerfileToBuildConfigDecorator(String name, Path pathToDockerfile) { super(name); + validate(pathToDockerfile); this.pathToDockerfile = pathToDockerfile; } + private void validate(Path pathToDockerfile) { + File file = pathToDockerfile.toFile(); + if (!file.exists()) { + throw new IllegalArgumentException( + "Specified Dockerfile: '" + pathToDockerfile.toAbsolutePath().toString() + "' does not exist."); + } + if (!file.isFile()) { + throw new IllegalArgumentException( + "Specified Dockerfile: '" + pathToDockerfile.toAbsolutePath().toString() + "' is not a normal file."); + } + + try { + Stream lines = Files.lines(pathToDockerfile); + Optional fromLine = lines.filter(l -> !l.startsWith("#")).map(String::trim) + .filter(l -> l.startsWith("FROM")).findFirst(); + if (!fromLine.isPresent()) { + throw new IllegalArgumentException("Specified Dockerfile: '" + pathToDockerfile.toAbsolutePath().toString() + + "' does not contain a FROM directive"); + } + } catch (IOException e) { + throw new IllegalArgumentException( + "Unable to validate specified Dockerfile: '" + pathToDockerfile.toAbsolutePath().toString() + "'"); + } + } + @Override public void andThenVisit(final BuildConfigSpecFluent spec, ObjectMeta meta) { try (InputStream is = new FileInputStream(pathToDockerfile.toFile())) { diff --git a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftProcessor.java b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftProcessor.java index e0d9a806c9abee..ed0d451359bc87 100644 --- a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftProcessor.java +++ b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftProcessor.java @@ -29,21 +29,20 @@ import org.jboss.logging.Logger; -import io.dekorate.deps.kubernetes.api.model.HasMetadata; -import io.dekorate.deps.kubernetes.api.model.KubernetesList; -import io.dekorate.deps.kubernetes.api.model.Secret; -import io.dekorate.deps.kubernetes.client.KubernetesClient; -import io.dekorate.deps.kubernetes.client.dsl.LogWatch; -import io.dekorate.deps.openshift.api.model.Build; -import io.dekorate.deps.openshift.api.model.BuildConfig; -import io.dekorate.deps.openshift.api.model.ImageStream; -import io.dekorate.deps.openshift.client.OpenShiftClient; import io.dekorate.utils.Clients; import io.dekorate.utils.Packaging; 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.Secret; import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; -import io.quarkus.bootstrap.model.AppDependency; +import io.fabric8.kubernetes.client.dsl.LogWatch; +import io.fabric8.openshift.api.model.Build; +import io.fabric8.openshift.api.model.BuildConfig; +import io.fabric8.openshift.api.model.ImageStream; +import io.fabric8.openshift.client.OpenShiftClient; import io.quarkus.container.image.deployment.ContainerImageConfig; import io.quarkus.container.image.deployment.util.ImageUtil; import io.quarkus.container.spi.AvailableContainerImageExtensionBuildItem; @@ -132,13 +131,11 @@ public void openshiftRequirementsJvm(OpenshiftConfig openshiftConfig, BuildProducer commandProducer) { OpenshiftConfig config = mergeConfig(openshiftConfig, s2iConfig); - final List appDeps = curateOutcomeBuildItem.getEffectiveModel().getUserDependencies(); String outputJarFileName = jarBuildItem.getPath().getFileName().toString(); String jarFileName = config.jarFileName.orElse(outputJarFileName); builderImageProducer.produce(new BaseImageInfoBuildItem(config.baseJvmImage)); Optional baseImage = OpenshiftBaseJavaImage.findMatching(config.baseJvmImage); - boolean libRequired = !packageConfig.type.equalsIgnoreCase(PackageConfig.UBER_JAR); if (config.buildStrategy == BuildStrategy.BINARY) { // Jar directory priorities: @@ -148,32 +145,18 @@ public void openshiftRequirementsJvm(OpenshiftConfig openshiftConfig, String jarDirectory = config.jarDirectory .orElse(baseImage.map(i -> i.getJarDirectory()).orElse(config.FALLBACK_JAR_DIRECTORY)); String pathToJar = concatUnixPaths(jarDirectory, jarFileName); - String classpath = appDeps.stream() - .map(d -> d.getArtifact().getGroupId() + "." + d.getArtifact().getPath().getFileName()) - .map(s -> concatUnixPaths(jarDirectory, "lib", s)) - .collect(Collectors.joining(":")); // If the image is known, we can define env vars for classpath, jar, lib etc. baseImage.ifPresent(b -> { envProducer.produce(KubernetesEnvBuildItem.createSimpleVar(b.getJarEnvVar(), pathToJar, null)); - if (libRequired) { - envProducer.produce(KubernetesEnvBuildItem.createSimpleVar(b.getJarLibEnvVar(), - concatUnixPaths(jarDirectory, "lib"), null)); - envProducer.produce(KubernetesEnvBuildItem.createSimpleVar(b.getClasspathEnvVar(), classpath, null)); - } envProducer.produce(KubernetesEnvBuildItem.createSimpleVar(b.getJvmOptionsEnvVar(), String.join(" ", config.jvmArguments), null)); }); //In all other cases its the responsibility of the image to set those up correctly. if (!baseImage.isPresent()) { List args = new ArrayList<>(); - args.addAll(Arrays.asList("-jar", pathToJar)); - if (libRequired) { - args.addAll(Arrays.asList("-cp", classpath)); - envProducer.produce( - KubernetesEnvBuildItem.createSimpleVar("JAVA_LIB_DIR", concatUnixPaths(jarDirectory, "lib"), null)); - } args.addAll(config.jvmArguments); + args.addAll(Arrays.asList("-jar", pathToJar)); envProducer.produce(KubernetesEnvBuildItem.createSimpleVar("JAVA_APP_JAR", pathToJar, null)); commandProducer.produce(new KubernetesCommandBuildItem("java", args.toArray(new String[args.size()]))); } diff --git a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftUtils.java b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftUtils.java index 0d3da3115e93c6..0c28b58a0d79c9 100644 --- a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftUtils.java +++ b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftUtils.java @@ -12,13 +12,13 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import io.dekorate.deps.kubernetes.api.model.HasMetadata; -import io.dekorate.deps.kubernetes.api.model.KubernetesListBuilder; -import io.dekorate.deps.kubernetes.api.model.ObjectReference; -import io.dekorate.deps.openshift.api.model.ImageStreamTag; -import io.dekorate.deps.openshift.api.model.SourceBuildStrategyFluent; -import io.dekorate.deps.openshift.client.OpenShiftClient; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.ObjectReference; +import io.fabric8.openshift.api.model.ImageStreamTag; +import io.fabric8.openshift.api.model.SourceBuildStrategyFluent; +import io.fabric8.openshift.client.OpenShiftClient; /** * This class is copied from Dekorate, with the difference that the {@code waitForImageStreamTags} method diff --git a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/RemoveEnvVarDecorator.java b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/RemoveEnvVarDecorator.java index 0864977fbad7e6..2724b8be295afd 100644 --- a/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/RemoveEnvVarDecorator.java +++ b/extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/RemoveEnvVarDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.container.image.openshift.deployment; -import io.dekorate.deps.kubernetes.api.model.ContainerFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ContainerFluent; public class RemoveEnvVarDecorator extends ApplicationContainerDecorator> { diff --git a/extensions/container-image/container-image-s2i/deployment/pom.xml b/extensions/container-image/container-image-s2i/deployment/pom.xml index 549ed4feea96f1..e717ba19077b3c 100644 --- a/extensions/container-image/container-image-s2i/deployment/pom.xml +++ b/extensions/container-image/container-image-s2i/deployment/pom.xml @@ -16,7 +16,7 @@ io.quarkus - quarkus-kubernetes-client-deployment-internal + quarkus-kubernetes-client-internal-deployment io.quarkus diff --git a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/RemoveEnvVarDecorator.java b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/RemoveEnvVarDecorator.java index 4f9eb0ee4a837a..cd16d3c53f4516 100644 --- a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/RemoveEnvVarDecorator.java +++ b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/RemoveEnvVarDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.container.image.s2i; -import io.dekorate.deps.kubernetes.api.model.ContainerFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ContainerFluent; public class RemoveEnvVarDecorator extends ApplicationContainerDecorator> { diff --git a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/ApplyDockerfileToBuildConfigDecorator.java b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/ApplyDockerfileToBuildConfigDecorator.java index 59ec79acc07798..aadde3e3166f25 100644 --- a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/ApplyDockerfileToBuildConfigDecorator.java +++ b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/ApplyDockerfileToBuildConfigDecorator.java @@ -6,9 +6,9 @@ import java.io.InputStream; import java.nio.file.Path; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.openshift.api.model.BuildConfigSpecFluent; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.openshift.api.model.BuildConfigSpecFluent; import io.quarkus.deployment.util.FileUtil; public class ApplyDockerfileToBuildConfigDecorator extends NamedResourceDecorator> { diff --git a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/PackageUtil.java b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/PackageUtil.java index 1b6d1b067b7db0..c66340af4c797c 100644 --- a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/PackageUtil.java +++ b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/PackageUtil.java @@ -17,11 +17,11 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.jsoup.helper.StringUtil; import io.dekorate.DekorateException; -import io.dekorate.deps.commons.compress.archivers.tar.TarArchiveEntry; -import io.dekorate.deps.commons.compress.archivers.tar.TarArchiveOutputStream; public class PackageUtil { diff --git a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iProcessor.java b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iProcessor.java index 4995a967739451..630853e4cee470 100644 --- a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iProcessor.java +++ b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iProcessor.java @@ -26,19 +26,19 @@ import org.jboss.logging.Logger; -import io.dekorate.deps.kubernetes.api.model.HasMetadata; -import io.dekorate.deps.kubernetes.api.model.KubernetesList; -import io.dekorate.deps.kubernetes.api.model.Secret; -import io.dekorate.deps.kubernetes.client.KubernetesClient; -import io.dekorate.deps.kubernetes.client.dsl.LogWatch; -import io.dekorate.deps.openshift.api.model.Build; -import io.dekorate.deps.openshift.api.model.BuildConfig; -import io.dekorate.deps.openshift.api.model.ImageStream; -import io.dekorate.deps.openshift.client.OpenShiftClient; import io.dekorate.utils.Clients; 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.Secret; import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.LogWatch; +import io.fabric8.openshift.api.model.Build; +import io.fabric8.openshift.api.model.BuildConfig; +import io.fabric8.openshift.api.model.ImageStream; +import io.fabric8.openshift.client.OpenShiftClient; import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.container.image.deployment.ContainerImageConfig; import io.quarkus.container.image.deployment.util.ImageUtil; diff --git a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iUtils.java b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iUtils.java index 2dc197c4e57be0..b473418487ad98 100644 --- a/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iUtils.java +++ b/extensions/container-image/container-image-s2i/deployment/src/main/java/io/quarkus/container/image/s2i/deployment/S2iUtils.java @@ -5,13 +5,13 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import io.dekorate.deps.kubernetes.api.model.HasMetadata; -import io.dekorate.deps.kubernetes.api.model.KubernetesListBuilder; -import io.dekorate.deps.kubernetes.api.model.ObjectReference; -import io.dekorate.deps.openshift.api.model.ImageStreamTag; -import io.dekorate.deps.openshift.api.model.SourceBuildStrategyFluent; -import io.dekorate.deps.openshift.client.OpenShiftClient; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.ObjectReference; +import io.fabric8.openshift.api.model.ImageStreamTag; +import io.fabric8.openshift.api.model.SourceBuildStrategyFluent; +import io.fabric8.openshift.client.OpenShiftClient; /** * This class is copied from Dekorate, with the difference that the {@code waitForImageStreamTags} method diff --git a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java index 19ec6c3b2ef8cf..2cffec7a41db70 100644 --- a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java +++ b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.testcontainers.containers.Db2Container; +import org.testcontainers.utility.DockerImageName; import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider; @@ -21,10 +22,11 @@ DevServicesDatasourceProviderBuildItem setupDB2() { public RunningDevServicesDatasource startDatabase(Optional username, Optional password, Optional datasourceName, Optional imageName, Map additionalProperties) { Db2Container container = new Db2Container( - imageName.orElse("ibmcom/db2:" + Db2Container.DEFAULT_TAG)) - .withPassword(password.orElse("quarkus")) - .withUsername(username.orElse("quarkus")) - .withDatabaseName(datasourceName.orElse("default")); + DockerImageName.parse(imageName.orElse("ibmcom/db2:" + Db2Container.DEFAULT_TAG)) + .asCompatibleSubstituteFor(DockerImageName.parse("ibmcom/db2"))) + .withPassword(password.orElse("quarkus")) + .withUsername(username.orElse("quarkus")) + .withDatabaseName(datasourceName.orElse("default")); additionalProperties.forEach(container::withUrlParam); container.start(); return new RunningDevServicesDatasource(container.getJdbcUrl(), container.getUsername(), diff --git a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/postgresql/deployment/MariaDBDevServicesProcessor.java b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/postgresql/deployment/MariaDBDevServicesProcessor.java index f3f9a9ea0ddda7..d2c790e60d3eda 100644 --- a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/postgresql/deployment/MariaDBDevServicesProcessor.java +++ b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/postgresql/deployment/MariaDBDevServicesProcessor.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.utility.DockerImageName; import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider; @@ -21,10 +22,11 @@ DevServicesDatasourceProviderBuildItem setupMariaDB() { public RunningDevServicesDatasource startDatabase(Optional username, Optional password, Optional datasourceName, Optional imageName, Map additionalProperties) { MariaDBContainer container = new MariaDBContainer( - imageName.orElse(MariaDBContainer.IMAGE + ":" + MariaDBContainer.DEFAULT_TAG)) - .withPassword(password.orElse("quarkus")) - .withUsername(username.orElse("quarkus")) - .withDatabaseName(datasourceName.orElse("default")); + DockerImageName.parse(imageName.orElse(MariaDBContainer.IMAGE + ":" + MariaDBContainer.DEFAULT_TAG)) + .asCompatibleSubstituteFor(DockerImageName.parse(MariaDBContainer.IMAGE))) + .withPassword(password.orElse("quarkus")) + .withUsername(username.orElse("quarkus")) + .withDatabaseName(datasourceName.orElse("default")); additionalProperties.forEach(container::withUrlParam); container.start(); return new RunningDevServicesDatasource(container.getJdbcUrl(), container.getUsername(), diff --git a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java index 6b2bddc178fee0..6a9c6c35301b0d 100644 --- a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java +++ b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java @@ -7,6 +7,7 @@ import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MSSQLServerContainer; +import org.testcontainers.utility.DockerImageName; import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider; @@ -22,8 +23,10 @@ DevServicesDatasourceProviderBuildItem setupMSSQL() { public RunningDevServicesDatasource startDatabase(Optional username, Optional password, Optional datasourceName, Optional imageName, Map additionalProperties) { JdbcDatabaseContainer container = new MSSQLServerContainer( - imageName.orElse(MSSQLServerContainer.IMAGE + ":" + MSSQLServerContainer.DEFAULT_TAG)) - .withPassword(password.orElse("Quarkuspassword1")); + DockerImageName + .parse(imageName.orElse(MSSQLServerContainer.IMAGE + ":" + MSSQLServerContainer.DEFAULT_TAG)) + .asCompatibleSubstituteFor(MSSQLServerContainer.IMAGE)) + .withPassword(password.orElse("Quarkuspassword1")); additionalProperties.forEach(container::withUrlParam); container.start(); return new RunningDevServicesDatasource(container.getJdbcUrl(), container.getUsername(), diff --git a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/postgresql/deployment/MySQLDevServicesProcessor.java b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/postgresql/deployment/MySQLDevServicesProcessor.java index 3058d557396101..905f3ada87f269 100644 --- a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/postgresql/deployment/MySQLDevServicesProcessor.java +++ b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/postgresql/deployment/MySQLDevServicesProcessor.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider; @@ -21,10 +22,11 @@ DevServicesDatasourceProviderBuildItem setupMysql() { public RunningDevServicesDatasource startDatabase(Optional username, Optional password, Optional datasourceName, Optional imageName, Map additionalProperties) { MySQLContainer container = new MySQLContainer( - imageName.orElse(MySQLContainer.IMAGE + ":" + MySQLContainer.DEFAULT_TAG)) - .withPassword(password.orElse("quarkus")) - .withUsername(username.orElse("quarkus")) - .withDatabaseName(datasourceName.orElse("default")); + DockerImageName.parse(imageName.orElse(MySQLContainer.IMAGE + ":" + MySQLContainer.DEFAULT_TAG)) + .asCompatibleSubstituteFor(DockerImageName.parse(MySQLContainer.IMAGE))) + .withPassword(password.orElse("quarkus")) + .withUsername(username.orElse("quarkus")) + .withDatabaseName(datasourceName.orElse("default")); additionalProperties.forEach(container::withUrlParam); container.start(); return new RunningDevServicesDatasource(container.getJdbcUrl(), container.getUsername(), diff --git a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java index 310d41b5580933..f28168ab46f2be 100644 --- a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java +++ b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider; @@ -14,6 +15,8 @@ public class PostgresqlDevServicesProcessor { + public static final String DEFAULT = PostgreSQLContainer.IMAGE + ":" + PostgreSQLContainer.DEFAULT_TAG; + @BuildStep DevServicesDatasourceProviderBuildItem setupPostgres() { return new DevServicesDatasourceProviderBuildItem(DatabaseKind.POSTGRESQL, new DevServicesDatasourceProvider() { @@ -21,10 +24,11 @@ DevServicesDatasourceProviderBuildItem setupPostgres() { public RunningDevServicesDatasource startDatabase(Optional username, Optional password, Optional datasourceName, Optional imageName, Map additionalProperties) { PostgreSQLContainer container = new PostgreSQLContainer( - imageName.orElse(PostgreSQLContainer.IMAGE + ":" + PostgreSQLContainer.DEFAULT_TAG)) - .withPassword(password.orElse("quarkus")) - .withUsername(username.orElse("quarkus")) - .withDatabaseName(datasourceName.orElse("default")); + DockerImageName.parse(imageName.orElse(DEFAULT)) + .asCompatibleSubstituteFor(DockerImageName.parse(PostgreSQLContainer.IMAGE))) + .withPassword(password.orElse("quarkus")) + .withUsername(username.orElse("quarkus")) + .withDatabaseName(datasourceName.orElse("default")); additionalProperties.forEach(container::withUrlParam); container.start(); return new RunningDevServicesDatasource(container.getJdbcUrl(), container.getUsername(), diff --git a/extensions/elasticsearch-rest-client-common/deployment/pom.xml b/extensions/elasticsearch-rest-client-common/deployment/pom.xml index c56ddb213e7638..5d076759ec98f7 100644 --- a/extensions/elasticsearch-rest-client-common/deployment/pom.xml +++ b/extensions/elasticsearch-rest-client-common/deployment/pom.xml @@ -17,6 +17,10 @@ io.quarkus quarkus-core-deployment + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-elasticsearch-rest-client-common diff --git a/extensions/elasticsearch-rest-client-common/runtime/pom.xml b/extensions/elasticsearch-rest-client-common/runtime/pom.xml index ad08b3c2b8693d..a22223eba8aa31 100644 --- a/extensions/elasticsearch-rest-client-common/runtime/pom.xml +++ b/extensions/elasticsearch-rest-client-common/runtime/pom.xml @@ -27,6 +27,10 @@ + + io.quarkus + quarkus-apache-httpclient + org.jboss.logging commons-logging-jboss-logging diff --git a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java index 8d1df9bf7e7700..5f631fc2dc1c8d 100644 --- a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java +++ b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java @@ -1,24 +1,42 @@ package io.quarkus.elasticsearch.restclient.lowlevel.deployment; +import org.jboss.jandex.DotName; + import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem; +import io.quarkus.arc.processor.DotNames; import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.elasticsearch.restclient.lowlevel.ElasticsearchClientConfig; import io.quarkus.elasticsearch.restclient.lowlevel.runtime.ElasticsearchRestClientProducer; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; class ElasticsearchLowLevelClientProcessor { + private static final DotName ELASTICSEARCH_CLIENT_CONFIG = DotName.createSimple(ElasticsearchClientConfig.class.getName()); + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(Feature.ELASTICSEARCH_REST_CLIENT); } - @BuildStep() + @BuildStep AdditionalBeanBuildItem build() { return AdditionalBeanBuildItem.unremovableOf(ElasticsearchRestClientProducer.class); } + @BuildStep + void elasticsearchClientConfigSupport(BuildProducer additionalBeans, + BuildProducer beanDefiningAnnotations) { + // add the @ElasticsearchClientConfig class otherwise it won't be registered as a qualifier + additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(ElasticsearchClientConfig.class).build()); + + beanDefiningAnnotations + .produce(new BeanDefiningAnnotationBuildItem(ELASTICSEARCH_CLIENT_CONFIG, DotNames.APPLICATION_SCOPED, false)); + } + @BuildStep HealthBuildItem addHealthCheck(ElasticsearchBuildTimeConfig buildTimeConfig) { return new HealthBuildItem("io.quarkus.elasticsearch.restclient.lowlevel.runtime.health.ElasticsearchHealthCheck", diff --git a/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchClientConfigTest.java b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchClientConfigTest.java index 06c308031e99f6..2f56c48eff7c35 100644 --- a/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchClientConfigTest.java +++ b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchClientConfigTest.java @@ -26,30 +26,24 @@ public class ElasticsearchClientConfigTest { @Inject ElasticsearchConfig config; - @Inject - @ElasticsearchClientConfig - TestConfigurator testConfigurator; @Test public void testRestClientBuilderHelperWithElasticsearchClientConfig() { RestClientBuilderHelper.createRestClientBuilder(config).build(); - assertTrue(testConfigurator.isInvoked()); + assertTrue(TestConfigurator.invoked); } @ElasticsearchClientConfig @ApplicationScoped public static class TestConfigurator implements RestClientBuilder.HttpClientConfigCallback { - private boolean invoked = false; + + private static boolean invoked = false; @Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder builder) { invoked = true; return builder; } - - public boolean isInvoked() { - return invoked; - } } } diff --git a/extensions/elytron-security-properties-file/deployment/pom.xml b/extensions/elytron-security-properties-file/deployment/pom.xml index 455b9eef52ec63..860ea5329ce9fe 100644 --- a/extensions/elytron-security-properties-file/deployment/pom.xml +++ b/extensions/elytron-security-properties-file/deployment/pom.xml @@ -23,10 +23,6 @@ quarkus-resteasy-deployment test - - io.quarkus - quarkus-vertx-http-deployment - io.quarkus quarkus-elytron-security-deployment diff --git a/extensions/elytron-security-properties-file/runtime/pom.xml b/extensions/elytron-security-properties-file/runtime/pom.xml index b82e6c5451ac1b..e4505220400e7f 100644 --- a/extensions/elytron-security-properties-file/runtime/pom.xml +++ b/extensions/elytron-security-properties-file/runtime/pom.xml @@ -17,10 +17,6 @@ io.quarkus quarkus-core - - io.quarkus - quarkus-vertx-http - io.quarkus quarkus-elytron-security diff --git a/extensions/elytron-security/runtime/src/main/java/io/quarkus/elytron/security/runtime/ElytronRecorder.java b/extensions/elytron-security/runtime/src/main/java/io/quarkus/elytron/security/runtime/ElytronRecorder.java index afb6ea0fca1ae9..3c005442adc63b 100644 --- a/extensions/elytron-security/runtime/src/main/java/io/quarkus/elytron/security/runtime/ElytronRecorder.java +++ b/extensions/elytron-security/runtime/src/main/java/io/quarkus/elytron/security/runtime/ElytronRecorder.java @@ -5,6 +5,7 @@ import javax.enterprise.inject.spi.CDI; import org.jboss.logging.Logger; +import org.wildfly.security.auth.server.RealmMapper; import org.wildfly.security.auth.server.SecurityDomain; import org.wildfly.security.auth.server.SecurityRealm; import org.wildfly.security.authz.AuthorizationIdentity; @@ -68,6 +69,9 @@ public boolean implies(Permission permission) { }; } }); + if (CDI.current().select(RealmMapper.class).isResolvable()) { + domain.setRealmMapper(CDI.current().select(RealmMapper.class).get()); + } return new RuntimeValue<>(domain); } @@ -81,7 +85,12 @@ public boolean implies(Permission permission) { * @param realm - the runtime value for the SecurityRealm */ public void addRealm(RuntimeValue builder, String realmName, RuntimeValue realm) { - builder.getValue().addRealm(realmName, realm.getValue()); + builder.getValue().addRealm(realmName, realm.getValue()).setRoleDecoder(new RoleDecoder() { + @Override + public Roles decodeRoles(AuthorizationIdentity authorizationIdentity) { + return CDI.current().select(DefaultRoleDecoder.class).get().decodeRoles(authorizationIdentity); + } + }).build(); } /** diff --git a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/devconsole/DevConsoleProcessor.java b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/devconsole/DevConsoleProcessor.java index bbc46c6d59a428..aecea632590046 100644 --- a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/devconsole/DevConsoleProcessor.java +++ b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/devconsole/DevConsoleProcessor.java @@ -20,6 +20,6 @@ public DevConsoleRuntimeTemplateInfoBuildItem collectBeanInfo() { @BuildStep @Record(value = RUNTIME_INIT, optional = true) DevConsoleRouteBuildItem invokeEndpoint(FlywayDevConsoleRecorder recorder) { - return new DevConsoleRouteBuildItem("containers", "POST", recorder.handler()); + return new DevConsoleRouteBuildItem("datasources", "POST", recorder.handler()); } } diff --git a/extensions/funqy/funqy-amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/funqy/funqy-amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 3f17a02242044d..cb395e98d2648e 100644 --- a/extensions/funqy/funqy-amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/funqy/funqy-amazon-lambda/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -14,4 +14,4 @@ metadata: name: "funqy-amazon-lambda" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 6edd7d48904e6a..d4e9df67e64531 100644 --- a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -15,4 +15,4 @@ metadata: name: "funqy-google-cloud-functions-example" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" \ No newline at end of file + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" \ No newline at end of file diff --git a/extensions/funqy/funqy-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/funqy/funqy-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 5513c07da82135..2aeee3ab9503c8 100644 --- a/extensions/funqy/funqy-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/funqy/funqy-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -12,4 +12,4 @@ metadata: name: "funqy-http" kind: "example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 2a97adcc96f8fd..73e6df3faf1c39 100644 --- a/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/funqy/funqy-knative-events/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -13,4 +13,4 @@ metadata: name: "funqy-knative-events" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/google-cloud-functions-http/runtime/pom.xml b/extensions/google-cloud-functions-http/runtime/pom.xml index afaf187dcc7074..5dd813408f4ea2 100644 --- a/extensions/google-cloud-functions-http/runtime/pom.xml +++ b/extensions/google-cloud-functions-http/runtime/pom.xml @@ -32,6 +32,26 @@ functions-framework-api provided + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + diff --git a/extensions/google-cloud-functions-http/runtime/src/main/java/io/quarkus/gcp/functions/http/QuarkusHttpFunction.java b/extensions/google-cloud-functions-http/runtime/src/main/java/io/quarkus/gcp/functions/http/QuarkusHttpFunction.java index e761ea71cb49e7..cc491f6b2da641 100644 --- a/extensions/google-cloud-functions-http/runtime/src/main/java/io/quarkus/gcp/functions/http/QuarkusHttpFunction.java +++ b/extensions/google-cloud-functions-http/runtime/src/main/java/io/quarkus/gcp/functions/http/QuarkusHttpFunction.java @@ -92,7 +92,7 @@ private void nettyDispatch(HttpRequest request, HttpResponse response) String path = request.getPath(); Optional host = request.getFirstHeader("Host"); DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.valueOf(request.getMethod()), path); + HttpMethod.valueOf(request.getMethod()), request.getQuery().map(q -> path + "?" + q).orElse(path)); if (host.isPresent()) { nettyRequest.headers().set("Host", host.get()); } @@ -142,6 +142,7 @@ public void handleMessage(Object msg) { try { if (msg instanceof io.netty.handler.codec.http.HttpResponse) { io.netty.handler.codec.http.HttpResponse res = (io.netty.handler.codec.http.HttpResponse) msg; + response.setStatusCode(res.status().code(), res.status().reasonPhrase()); for (Map.Entry entry : res.headers()) { response.appendHeader(entry.getKey(), entry.getValue()); } diff --git a/extensions/google-cloud-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/google-cloud-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 977d107cd6f2e9..4ef1887a077e79 100644 --- a/extensions/google-cloud-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/google-cloud-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -14,4 +14,4 @@ metadata: name: "google-cloud-functions-http" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/google-cloud-functions-http/runtime/src/test/java/io/quarkus/gcp/functions/http/QuarkusHttpFunctionTest.java b/extensions/google-cloud-functions-http/runtime/src/test/java/io/quarkus/gcp/functions/http/QuarkusHttpFunctionTest.java new file mode 100644 index 00000000000000..1e8d60ec757fa5 --- /dev/null +++ b/extensions/google-cloud-functions-http/runtime/src/test/java/io/quarkus/gcp/functions/http/QuarkusHttpFunctionTest.java @@ -0,0 +1,109 @@ +package io.quarkus.gcp.functions.http; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; + +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.quarkus.netty.runtime.virtual.VirtualClientConnection; +import io.quarkus.netty.runtime.virtual.VirtualResponseHandler; +import io.quarkus.runtime.Application; + +public class QuarkusHttpFunctionTest { + private static final long PROCESSING_TIMEOUT = TimeUnit.SECONDS.toMillis(1); + + private static final String PATH = "/test/path"; + private static final String QUERY = "testParam1=testValue1&testParam2=testValue2"; + private static final String HOST_HEADER = "Host"; + private static final String HOST = "localhost"; + private static final String METHOD = "GET"; + + private final Application application = mock(Application.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); + private final VirtualClientConnection connection = mock(VirtualClientConnection.class); + + @BeforeEach + public void mockSetup() { + when(request.getPath()).thenReturn(PATH); + when(request.getFirstHeader(eq(HOST_HEADER))).thenReturn(Optional.of(HOST)); + when(request.getHeaders()).thenReturn(Collections.singletonMap(HOST_HEADER, Collections.singletonList(HOST))); + when(request.getMethod()).thenReturn(METHOD); + } + + @SuppressWarnings({ "OptionalUsedAsFieldOrParameterType", "rawtypes", "unused" }) + private void mockHttpFunction(Optional query, HttpResponseStatus status) { + when(request.getQuery()).thenReturn(query); + try (MockedStatic applicationMock = Mockito.mockStatic(Application.class)) { + applicationMock.when(Application::currentApplication).thenReturn(application); + QuarkusHttpFunction function = new QuarkusHttpFunction(); + CompletableFuture requestFuture = CompletableFuture.supplyAsync(() -> { + try (MockedStatic connectionMock = Mockito.mockStatic(VirtualClientConnection.class)) { + connectionMock.when(() -> VirtualClientConnection.connect(any(), any())).thenAnswer(i -> { + VirtualResponseHandler handler = i.getArgument(0); + CompletableFuture responseFuture = CompletableFuture.supplyAsync(() -> { + handler.handleMessage(new DefaultHttpResponse(HttpVersion.HTTP_1_1, status)); + return null; + }); + return connection; + }); + function.service(request, response); + } catch (IOException ignore) { + } + return null; + }); + } + } + + public static Iterable queries() { + return Arrays.asList(new Object[] { Optional.of(QUERY), PATH + "?" + QUERY }, new Object[] { Optional.empty(), PATH }); + } + + @ParameterizedTest + @MethodSource("queries") + @SuppressWarnings({ "OptionalUsedAsFieldOrParameterType" }) + public void verifyQueryParametersBypass(Optional query, String expected) { + mockHttpFunction(query, HttpResponseStatus.OK); + ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(captor.capture()); + DefaultHttpRequest rq = (DefaultHttpRequest) captor.getAllValues().get(0); + assertEquals(expected, rq.uri()); + } + + public static Iterable responses() { + return Arrays.asList(new Object[] { HttpResponseStatus.CREATED }, new Object[] { HttpResponseStatus.OK }, + new Object[] { HttpResponseStatus.BAD_REQUEST }); + } + + @ParameterizedTest + @MethodSource("responses") + public void verifyResponseStatusBypass(final HttpResponseStatus status) { + mockHttpFunction(Optional.empty(), status); + verify(connection, timeout(PROCESSING_TIMEOUT).times(2)).sendMessage(any()); + verify(response, timeout(PROCESSING_TIMEOUT)).setStatusCode(eq(status.code()), eq(status.reasonPhrase())); + } +} diff --git a/extensions/google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 1f487b0e0caae1..6a8bf494547e62 100755 --- a/extensions/google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/google-cloud-functions/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -13,4 +13,4 @@ metadata: name: "google-cloud-functions" kind: "singleton-example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientProcessor.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientProcessor.java index b4eca2f58b59a7..adb67d9fe1e890 100644 --- a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientProcessor.java +++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; @@ -175,6 +176,12 @@ void registerSslResources(BuildProducer resourceBu registerResourcesForProperties(config, resourceBuildItem, TRUST_STORE_PATTERN, CERTIFICATE_PATTERN, KEY_PATTERN); } + @BuildStep + void runtimeInitialize(BuildProducer producer) { + // io.grpc.internal.RetriableStream uses j.u.Ramdom, so needs to be runtime-initialized + producer.produce(new RuntimeInitializedClassBuildItem("io.grpc.internal.RetriableStream")); + } + private void generateChannelProducer(MethodCreator mc, GrpcServiceBuildItem svc) { ResultHandle name = mc.load(svc.getServiceName()); ResultHandle result = mc.invokeStaticMethod(CREATE_CHANNEL_METHOD, name); 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 447c88138c3b87..a8848d7e5b33d5 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,6 +28,7 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; @@ -95,6 +96,7 @@ void buildContainerBean(BuildProducer beans, @Record(value = ExecutionTime.RUNTIME_INIT) ServiceStartBuildItem build(GrpcServerRecorder recorder, GrpcConfiguration config, ShutdownContextBuildItem shutdown, List bindables, + LaunchModeBuildItem launchModeBuildItem, VertxBuildItem vertx) { // Build the list of blocking methods per service implementation @@ -106,7 +108,7 @@ ServiceStartBuildItem build(GrpcServerRecorder recorder, GrpcConfiguration confi } if (!bindables.isEmpty()) { - recorder.initializeGrpcServer(vertx.getVertx(), config, shutdown, blocking); + recorder.initializeGrpcServer(vertx.getVertx(), config, shutdown, blocking, launchModeBuildItem.getLaunchMode()); return new ServiceStartBuildItem(GRPC_SERVER); } return null; diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/GrpcServiceTestBase.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/GrpcServiceTestBase.java index 99cbc34fbc6c6a..4187a288719223 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/GrpcServiceTestBase.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/GrpcServiceTestBase.java @@ -37,7 +37,7 @@ public class GrpcServiceTestBase { @BeforeEach public void init() throws Exception { - channel = ManagedChannelBuilder.forAddress("localhost", 9000) + channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java index e11c4adaa6cc69..e58596f060f7e1 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java @@ -52,7 +52,7 @@ public void init() throws Exception { SslContext sslcontext = GrpcSslContexts.forClient() .trustManager(createTrustAllTrustManager()) .build(); - channel = NettyChannelBuilder.forAddress("localhost", 9000) + channel = NettyChannelBuilder.forAddress("localhost", 9001) .sslContext(sslcontext) .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java index d8d1c9937b6625..20e1c1bba1e3c5 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java @@ -54,7 +54,7 @@ public void init() throws Exception { SslContext sslcontext = GrpcSslContexts.forClient() .trustManager(createTrustAllTrustManager()) .build(); - channel = NettyChannelBuilder.forAddress("localhost", 9000) + channel = NettyChannelBuilder.forAddress("localhost", 9001) .sslContext(sslcontext) .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java index aeb0f28d596a09..ccc7cb2c5b655e 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java @@ -52,7 +52,7 @@ public void init() throws Exception { SslContext sslcontext = GrpcSslContexts.forClient() .trustManager(createTrustAllTrustManager()) .build(); - channel = NettyChannelBuilder.forAddress("localhost", 9000) + channel = NettyChannelBuilder.forAddress("localhost", 9001) .sslContext(sslcontext) .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/blocking/BlockingServiceTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/blocking/BlockingServiceTest.java index 4c1b1df3ab0223..dda97d631461a7 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/blocking/BlockingServiceTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/blocking/BlockingServiceTest.java @@ -52,7 +52,7 @@ public class BlockingServiceTest { @BeforeEach public void init() { - channel = ManagedChannelBuilder.forAddress("localhost", 9000) + channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityReversedTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityReversedTest.java index 140bbd7d1c0bad..f38aa2d51382ce 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityReversedTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityReversedTest.java @@ -40,7 +40,7 @@ public class ServerInterceptorPriorityReversedTest { @BeforeEach public void init() { - channel = ManagedChannelBuilder.forAddress("localhost", 9000) + channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityTest.java index a07f908a711fc8..c336b274a4667c 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorPriorityTest.java @@ -40,7 +40,7 @@ public class ServerInterceptorPriorityTest { @BeforeEach public void init() { - channel = ManagedChannelBuilder.forAddress("localhost", 9000) + channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorRegistrationTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorRegistrationTest.java index 8b10b21a0840b3..40fe92067cfe4c 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorRegistrationTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/ServerInterceptorRegistrationTest.java @@ -38,7 +38,7 @@ public class ServerInterceptorRegistrationTest { @BeforeEach public void init() throws Exception { - channel = ManagedChannelBuilder.forAddress("localhost", 9000) + channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/scaling/ScalingTestBase.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/scaling/ScalingTestBase.java index 2fb1bd8be28ac2..c4536e2c0606aa 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/scaling/ScalingTestBase.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/scaling/ScalingTestBase.java @@ -24,7 +24,7 @@ Set getThreadsUsedFor100Requests() throws InterruptedException, Executio List> calls = new ArrayList<>(); for (int i = 0; i < requestNo; i++) { calls.add(() -> { - ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9000) + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9001) .usePlaintext() .build(); HelloReply reply = GreeterGrpc.newBlockingStub(channel) diff --git a/extensions/grpc/deployment/src/test/resources/blocking-config.properties b/extensions/grpc/deployment/src/test/resources/blocking-config.properties index cef3311f67737d..d02ee9c19cf1ca 100644 --- a/extensions/grpc/deployment/src/test/resources/blocking-config.properties +++ b/extensions/grpc/deployment/src/test/resources/blocking-config.properties @@ -1,4 +1,7 @@ quarkus.grpc.clients.reflection-service.host=localhost +quarkus.grpc.clients.reflection-service.port=9001 quarkus.grpc.clients.test-service.host=localhost +quarkus.grpc.clients.test-service.port=9001 quarkus.grpc.clients.greeter-service.host=localhost +quarkus.grpc.clients.greeter-service.port=9001 quarkus.grpc.server.enable-reflection-service=true diff --git a/extensions/grpc/deployment/src/test/resources/blocking-test-config.properties b/extensions/grpc/deployment/src/test/resources/blocking-test-config.properties index 6077654bf8893c..837c460c4c396f 100644 --- a/extensions/grpc/deployment/src/test/resources/blocking-test-config.properties +++ b/extensions/grpc/deployment/src/test/resources/blocking-test-config.properties @@ -1,2 +1,3 @@ quarkus.grpc.clients.blocking-test.host=localhost +quarkus.grpc.clients.blocking-test.port=9001 quarkus.grpc.server.enable-reflection-service=true diff --git a/extensions/grpc/deployment/src/test/resources/call-from-blocking-service.properties b/extensions/grpc/deployment/src/test/resources/call-from-blocking-service.properties index a05a7c427c7353..90598821b6ebc3 100644 --- a/extensions/grpc/deployment/src/test/resources/call-from-blocking-service.properties +++ b/extensions/grpc/deployment/src/test/resources/call-from-blocking-service.properties @@ -1,2 +1,4 @@ quarkus.grpc.clients.greeter.host=localhost -quarkus.grpc.clients.service3.host=localhost \ No newline at end of file +quarkus.grpc.clients.greeter.port=9001 +quarkus.grpc.clients.service3.host=localhost +quarkus.grpc.clients.service3.port=9001 \ No newline at end of file diff --git a/extensions/grpc/deployment/src/test/resources/grpc-client-tls-configuration.properties b/extensions/grpc/deployment/src/test/resources/grpc-client-tls-configuration.properties index 2ffa41c43d2642..c393ab7d3fd1ef 100644 --- a/extensions/grpc/deployment/src/test/resources/grpc-client-tls-configuration.properties +++ b/extensions/grpc/deployment/src/test/resources/grpc-client-tls-configuration.properties @@ -1,4 +1,5 @@ quarkus.grpc.clients.hello.host=localhost +quarkus.grpc.clients.hello.port=9001 quarkus.grpc.clients.hello.ssl.trust-store=src/test/resources/tls-from-file/ca.pem quarkus.grpc.server.ssl.certificate=src/test/resources/tls-from-file/server.pem diff --git a/extensions/grpc/deployment/src/test/resources/health-config.properties b/extensions/grpc/deployment/src/test/resources/health-config.properties index 3b98375cca0a14..bc53c73d5adac0 100644 --- a/extensions/grpc/deployment/src/test/resources/health-config.properties +++ b/extensions/grpc/deployment/src/test/resources/health-config.properties @@ -1,3 +1,4 @@ quarkus.grpc.clients.health-service.host=localhost +quarkus.grpc.clients.health-service.port=9001 quarkus.grpc.clients.foo.host=localhost quarkus.grpc.server.port=9000 \ No newline at end of file diff --git a/extensions/grpc/deployment/src/test/resources/hello-config.properties b/extensions/grpc/deployment/src/test/resources/hello-config.properties index a470728522c1d5..a914a069aacd25 100644 --- a/extensions/grpc/deployment/src/test/resources/hello-config.properties +++ b/extensions/grpc/deployment/src/test/resources/hello-config.properties @@ -1,4 +1,5 @@ quarkus.grpc.clients.hello-service.host=localhost +quarkus.grpc.clients.hello-service.port=9001 quarkus.grpc.clients.hello-service.keep-alive-timeout=1s quarkus.grpc.clients.hello-service-2.host=localhost quarkus.grpc.clients.hello-service-2.retry=true \ No newline at end of file diff --git a/extensions/grpc/deployment/src/test/resources/multiple-instances-config.properties b/extensions/grpc/deployment/src/test/resources/multiple-instances-config.properties index a97643669296c4..5b608591877f75 100644 --- a/extensions/grpc/deployment/src/test/resources/multiple-instances-config.properties +++ b/extensions/grpc/deployment/src/test/resources/multiple-instances-config.properties @@ -1,3 +1,4 @@ quarkus.grpc.server.instances=3 quarkus.grpc.clients.hello-service.host=localhost +quarkus.grpc.clients.hello-service.port=9001 quarkus.grpc.clients.hello-service.keep-alive-timeout=1s \ No newline at end of file diff --git a/extensions/grpc/deployment/src/test/resources/reflection-config.properties b/extensions/grpc/deployment/src/test/resources/reflection-config.properties index c15965d5e3744e..fb8074812b64ea 100644 --- a/extensions/grpc/deployment/src/test/resources/reflection-config.properties +++ b/extensions/grpc/deployment/src/test/resources/reflection-config.properties @@ -1,2 +1,3 @@ quarkus.grpc.clients.reflection-service.host=localhost +quarkus.grpc.clients.reflection-service.port=9001 quarkus.grpc.server.enable-reflection-service=true \ No newline at end of file diff --git a/extensions/grpc/deployment/src/test/resources/single-instance-config.properties b/extensions/grpc/deployment/src/test/resources/single-instance-config.properties index 675cedf145c839..8faad84d555030 100644 --- a/extensions/grpc/deployment/src/test/resources/single-instance-config.properties +++ b/extensions/grpc/deployment/src/test/resources/single-instance-config.properties @@ -1,3 +1,4 @@ quarkus.grpc.server.instances=1 quarkus.grpc.clients.hello-service.host=localhost +quarkus.grpc.clients.hello-service.port=9001 quarkus.grpc.clients.hello-service.keep-alive-timeout=1s \ No newline at end of file diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java index 0ddaee2cc7b610..0cbbdca5d85c24 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java @@ -69,7 +69,7 @@ public class GrpcServerRecorder { public void initializeGrpcServer(RuntimeValue vertxSupplier, GrpcConfiguration cfg, ShutdownContext shutdown, - Map> blockingMethodsPerServiceImplementationClass) { + Map> blockingMethodsPerServiceImplementationClass, LaunchMode launchMode) { GrpcContainer grpcContainer = Arc.container().instance(GrpcContainer.class).get(); if (grpcContainer == null) { throw new IllegalStateException("gRPC not initialized, GrpcContainer not found"); @@ -83,29 +83,29 @@ public void initializeGrpcServer(RuntimeValue vertxSupplier, this.blockingMethodsPerService = blockingMethodsPerServiceImplementationClass; GrpcServerConfiguration configuration = cfg.server; - final boolean devMode = ProfileManager.getLaunchMode() == LaunchMode.DEVELOPMENT; - if (devMode) { + if (launchMode == LaunchMode.DEVELOPMENT) { // start single server, not in a verticle, regardless of the configuration.instances // for reason unknown to me, verticles occasionally get undeployed on dev mode reload if (GrpcServerReloader.getServer() == null) { - devModeStart(grpcContainer, vertx, configuration, shutdown); + devModeStart(grpcContainer, vertx, configuration, shutdown, launchMode); } else { devModeReload(grpcContainer); } } else { - prodStart(grpcContainer, vertx, configuration); + prodStart(grpcContainer, vertx, configuration, launchMode); } } - private void prodStart(GrpcContainer grpcContainer, Vertx vertx, GrpcServerConfiguration configuration) { + private void prodStart(GrpcContainer grpcContainer, Vertx vertx, GrpcServerConfiguration configuration, + LaunchMode launchMode) { CompletableFuture startResult = new CompletableFuture<>(); vertx.deployVerticle( new Supplier() { @Override public Verticle get() { - return new GrpcServerVerticle(configuration, grpcContainer); + return new GrpcServerVerticle(configuration, grpcContainer, launchMode); } }, new DeploymentOptions().setInstances(configuration.instances), @@ -115,7 +115,7 @@ public void handle(AsyncResult result) { if (result.failed()) { startResult.completeExceptionally(result.cause()); } else { - GrpcServerRecorder.this.postStartup(grpcContainer, configuration); + GrpcServerRecorder.this.postStartup(grpcContainer, configuration, launchMode == LaunchMode.TEST); startResult.complete(null); } @@ -134,7 +134,7 @@ public void handle(AsyncResult result) { } } - private void postStartup(GrpcContainer grpcContainer, GrpcServerConfiguration configuration) { + private void postStartup(GrpcContainer grpcContainer, GrpcServerConfiguration configuration, boolean test) { grpcContainer.getHealthStorage().stream().forEach(new Consumer() { //NOSONAR @Override public void accept(GrpcHealthStorage storage) { @@ -152,14 +152,14 @@ public void accept(BindableService service) { } }); LOGGER.infof("gRPC Server started on %s:%d [SSL enabled: %s]", - configuration.host, configuration.port, !configuration.plainText); + configuration.host, test ? configuration.testPort : configuration.port, !configuration.plainText); } private void devModeStart(GrpcContainer grpcContainer, Vertx vertx, GrpcServerConfiguration configuration, - ShutdownContext shutdown) { + ShutdownContext shutdown, LaunchMode launchMode) { CompletableFuture future = new CompletableFuture<>(); - VertxServer vertxServer = buildServer(vertx, configuration, grpcContainer, true) + VertxServer vertxServer = buildServer(vertx, configuration, grpcContainer, launchMode) .start(new Handler>() { // NOSONAR @Override public void handle(AsyncResult ar) { @@ -167,7 +167,7 @@ public void handle(AsyncResult ar) { LOGGER.error("Unable to start the gRPC server", ar.cause()); future.completeExceptionally(ar.cause()); } else { - postStartup(grpcContainer, configuration); + postStartup(grpcContainer, configuration, false); future.complete(true); grpcVerticleCount.incrementAndGet(); } @@ -281,9 +281,10 @@ public static int getVerticleCount() { } private VertxServer buildServer(Vertx vertx, GrpcServerConfiguration configuration, - GrpcContainer grpcContainer, boolean devMode) { + GrpcContainer grpcContainer, LaunchMode launchMode) { VertxServerBuilder builder = VertxServerBuilder - .forAddress(vertx, configuration.host, configuration.port); + .forAddress(vertx, configuration.host, + launchMode == LaunchMode.TEST ? configuration.testPort : configuration.port); AtomicBoolean usePlainText = new AtomicBoolean(); builder.useSsl(new Handler() { // NOSONAR @@ -352,7 +353,7 @@ public void handle(HttpServerOptions options) { builder.intercept(serverInterceptor); } - if (devMode) { + if (launchMode == LaunchMode.DEVELOPMENT) { builder.commandDecorator(new Consumer() { @Override public void accept(Runnable command) { @@ -374,7 +375,8 @@ public void handle(AsyncResult result) { } LOGGER.debugf("Starting gRPC Server on %s:%d [SSL enabled: %s]...", - configuration.host, configuration.port, !usePlainText.get()); + configuration.host, launchMode == LaunchMode.TEST ? configuration.testPort : configuration.port, + !usePlainText.get()); return builder.build(); } @@ -382,12 +384,14 @@ public void handle(AsyncResult result) { private class GrpcServerVerticle extends AbstractVerticle { private final GrpcServerConfiguration configuration; private final GrpcContainer grpcContainer; + private final LaunchMode launchMode; private VertxServer grpcServer; - GrpcServerVerticle(GrpcServerConfiguration configuration, GrpcContainer grpcContainer) { + GrpcServerVerticle(GrpcServerConfiguration configuration, GrpcContainer grpcContainer, LaunchMode launchMode) { this.configuration = configuration; this.grpcContainer = grpcContainer; + this.launchMode = launchMode; } @Override @@ -397,7 +401,7 @@ public void start(Promise startPromise) { "Unable to find bean exposing the `BindableService` interface - not starting the gRPC server"); return; } - grpcServer = buildServer(getVertx(), configuration, grpcContainer, false) + grpcServer = buildServer(getVertx(), configuration, grpcContainer, launchMode) .start(new Handler>() { // NOSONAR @Override public void handle(AsyncResult ar) { diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcClientConfiguration.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcClientConfiguration.java index 8801f5036e283f..0d1f94c7f5eea2 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcClientConfiguration.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcClientConfiguration.java @@ -137,4 +137,10 @@ public class GrpcClientConfiguration { @ConfigItem public Optional userAgent; + /** + * Use a custom load balancing policy. + * Accepted values are: {@code pick_value}, {@code round_robin}, {@code grpclb} + */ + @ConfigItem(defaultValue = "pick_first") + public String loadBalancingPolicy; } diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcServerConfiguration.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcServerConfiguration.java index 7e509fb1f1e440..96d78bf9daf2e4 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcServerConfiguration.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcServerConfiguration.java @@ -17,6 +17,12 @@ public class GrpcServerConfiguration { @ConfigItem(defaultValue = "9000") public int port; + /** + * The gRPC Server port used for tests. + */ + @ConfigItem(defaultValue = "9001") + public int testPort; + /** * The gRPC server host. */ diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/Channels.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/Channels.java index a9c86fd383ee9c..9fe04ee2489772 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/Channels.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/Channels.java @@ -86,6 +86,7 @@ public static Channel createChannel(String name) throws SSLException { } NettyChannelBuilder builder = NettyChannelBuilder.forAddress(host, port) + .defaultLoadBalancingPolicy(config.loadBalancingPolicy) .flowControlWindow(config.flowControlWindow.orElse(DEFAULT_FLOW_CONTROL_WINDOW)) .keepAliveWithoutCalls(config.keepAliveWithoutCalls) .maxHedgedAttempts(config.maxHedgedAttempts) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/transaction/AbstractTransactionLifecycleTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/transaction/AbstractTransactionLifecycleTest.java index ff9843b490874c..94ffe7d52e0086 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/transaction/AbstractTransactionLifecycleTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/transaction/AbstractTransactionLifecycleTest.java @@ -47,8 +47,9 @@ public abstract class AbstractTransactionLifecycleTest { .addAsResource("application.properties")) // Expect no warnings (in particular from Agroal) .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() - // Ignore this particular warning when building with Java 8: it's not relevant to this test. - && !record.getMessage().contains("Using Java versions older than 11 to build Quarkus applications")) + // Ignore these particular warnings: they are not relevant to this test. + && !record.getMessage().contains("Using Java versions older than 11 to build Quarkus applications") + && !record.getMessage().contains("Agroal does not support detecting if a connection is still usable")) .assertLogRecords(records -> assertThat(records) .extracting(LogRecord::getMessage) // This is just to get meaningful error messages, as LogRecord doesn't have a toString() .isEmpty()); diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index 36694f7f8cd6bf..e06bdcfb65c1aa 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -64,6 +64,13 @@ public final class HibernateReactiveProcessor { private static final String HIBERNATE_REACTIVE = "Hibernate Reactive"; + static final String[] REFLECTIVE_CONSTRUCTORS_NEEDED = { + "org.hibernate.reactive.persister.entity.impl.ReactiveSingleTableEntityPersister", + "org.hibernate.reactive.persister.entity.impl.ReactiveJoinedSubclassEntityPersister", + "org.hibernate.reactive.persister.entity.impl.ReactiveUnionSubclassEntityPersister", + "org.hibernate.reactive.persister.collection.impl.ReactiveOneToManyPersister", + "org.hibernate.reactive.persister.collection.impl.ReactiveBasicCollectionPersister", + }; @BuildStep FeatureBuildItem feature() { @@ -92,11 +99,7 @@ void registerBeans(BuildProducer additionalBeans, Combi @BuildStep void reflections(BuildProducer reflectiveClass) { - String[] classes = { - "org.hibernate.reactive.persister.entity.impl.ReactiveSingleTableEntityPersister", - "org.hibernate.reactive.persister.collection.impl.ReactiveOneToManyPersister" - }; - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, classes)); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, REFLECTIVE_CONSTRUCTORS_NEEDED)); } @BuildStep diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/deployment/PersisterNamesTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/deployment/PersisterNamesTest.java new file mode 100644 index 00000000000000..1ddcba421a3242 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/deployment/PersisterNamesTest.java @@ -0,0 +1,22 @@ +package io.quarkus.hibernate.reactive.deployment; + +import org.junit.jupiter.api.Test; + +public final class PersisterNamesTest { + + @Test + public void testClassesExist() { + for (String clazz : HibernateReactiveProcessor.REFLECTIVE_CONSTRUCTORS_NEEDED) { + assertClassExists(clazz); + } + } + + private void assertClassExists(String clazz) { + try { + Thread.currentThread().getContextClassLoader().loadClass(clazz); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/FastBootReactiveEntityManagerFactoryBuilder.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/FastBootReactiveEntityManagerFactoryBuilder.java index 68cd314d36e7ad..58aa3705a69b15 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/FastBootReactiveEntityManagerFactoryBuilder.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/FastBootReactiveEntityManagerFactoryBuilder.java @@ -6,6 +6,7 @@ import org.hibernate.boot.internal.SessionFactoryOptionsBuilder; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.reactive.bulk.impl.ReactiveBulkIdStrategy; import org.hibernate.reactive.session.impl.ReactiveSessionFactoryImpl; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; @@ -25,6 +26,8 @@ public FastBootReactiveEntityManagerFactoryBuilder(PrevalidatedQuarkusMetadata m @Override public EntityManagerFactory build() { final SessionFactoryOptionsBuilder optionsBuilder = metadata.buildSessionFactoryOptionsBuilder(); + optionsBuilder.enableCollectionInDefaultFetchGroup(true); + optionsBuilder.applyMultiTableBulkIdStrategy(new ReactiveBulkIdStrategy(metadata)); populate(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, optionsBuilder, standardServiceRegistry, multiTenancyStrategy); SessionFactoryOptions options = optionsBuilder.buildOptions(); diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/UserCountryNotSetValidatorLocaleTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/UserCountryNotSetValidatorLocaleTest.java index 47206adc1caed7..7eead10336d8bd 100644 --- a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/UserCountryNotSetValidatorLocaleTest.java +++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/UserCountryNotSetValidatorLocaleTest.java @@ -30,7 +30,9 @@ public void run() { }).setAfterAllCustomizer(new Runnable() { @Override public void run() { - System.setProperty("user.country", userCountry); + if (userCountry != null) { + System.setProperty("user.country", userCountry); + } System.setProperty("user.language", userLanguage); } }); diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index 03f7a9c89caeb5..f6252d4a620884 100755 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -57,14 +57,21 @@ public class JacksonProcessor { private static final DotName JSON_DESERIALIZE = DotName.createSimple(JsonDeserialize.class.getName()); + private static final DotName JSON_SERIALIZE = DotName.createSimple(JsonSerialize.class.getName()); + private static final DotName JSON_CREATOR = DotName.createSimple("com.fasterxml.jackson.annotation.JsonCreator"); + private static final DotName JSON_NAMING = DotName.createSimple("com.fasterxml.jackson.databind.annotation.JsonNaming"); + private static final DotName JSON_IDENTITY_INFO = DotName.createSimple("com.fasterxml.jackson.annotation.JsonIdentityInfo"); + private static final DotName BUILDER_VOID = DotName.createSimple(Void.class.getName()); private static final String TIME_MODULE = "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"; + private static final String JDK8_MODULE = "com.fasterxml.jackson.datatype.jdk8.Jdk8Module"; + private static final String PARAMETER_NAMES_MODULE = "com.fasterxml.jackson.module.paramnames.ParameterNamesModule"; // this list can probably be enriched with more modules @@ -86,7 +93,8 @@ CapabilityBuildItem register( reflectiveClass.produce( new ReflectiveClassBuildItem(true, false, "com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector", "com.fasterxml.jackson.databind.ser.std.SqlDateSerializer", - "com.fasterxml.jackson.databind.ser.std.SqlTimeSerializer")); + "com.fasterxml.jackson.databind.ser.std.SqlTimeSerializer", + "com.fasterxml.jackson.annotation.SimpleObjectIdResolver")); IndexView index = combinedIndexBuildItem.getIndex(); diff --git a/extensions/jdbc/jdbc-db2/deployment/pom.xml b/extensions/jdbc/jdbc-db2/deployment/pom.xml index 9e3d5bc84c2451..848db0271a49e8 100644 --- a/extensions/jdbc/jdbc-db2/deployment/pom.xml +++ b/extensions/jdbc/jdbc-db2/deployment/pom.xml @@ -33,6 +33,21 @@ io.quarkus quarkus-devservices-db2 + + io.quarkus + quarkus-agroal-deployment + test + + + io.quarkus + quarkus-junit5-internal + test + + + org.assertj + assertj-core + test + @@ -49,6 +64,35 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + test-devservices + + + test-containers + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/extensions/jdbc/jdbc-db2/deployment/src/test/java/io/quarkus/jdbc/db2/deployment/DevServicesDB2DatasourceTestCase.java b/extensions/jdbc/jdbc-db2/deployment/src/test/java/io/quarkus/jdbc/db2/deployment/DevServicesDB2DatasourceTestCase.java new file mode 100644 index 00000000000000..ff5504bca36bfe --- /dev/null +++ b/extensions/jdbc/jdbc-db2/deployment/src/test/java/io/quarkus/jdbc/db2/deployment/DevServicesDB2DatasourceTestCase.java @@ -0,0 +1,53 @@ +package io.quarkus.jdbc.db2.deployment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Connection; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.agroal.api.AgroalDataSource; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.exceptionsorter.DB2ExceptionSorter; +import io.quarkus.test.QuarkusUnitTest; + +public class DevServicesDB2DatasourceTestCase { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("container-license-acceptance.txt")) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); + + @Inject + AgroalDataSource dataSource; + + @Test + public void testDatasource() throws Exception { + AgroalConnectionPoolConfiguration configuration = dataSource.getConfiguration().connectionPoolConfiguration(); + assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:db2:")); + assertEquals("quarkus", configuration.connectionFactoryConfiguration().principal().getName()); + assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(DB2ExceptionSorter.class); + + try (Connection connection = dataSource.getConnection()) { + } + } +} diff --git a/extensions/jdbc/jdbc-db2/deployment/src/test/resources/container-license-acceptance.txt b/extensions/jdbc/jdbc-db2/deployment/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000000..c4ad0a38410dd9 --- /dev/null +++ b/extensions/jdbc/jdbc-db2/deployment/src/test/resources/container-license-acceptance.txt @@ -0,0 +1 @@ +ibmcom/db2:11.5.0.0a diff --git a/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java index 3939d6d46e8b7b..218243f479eff5 100644 --- a/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java @@ -1,9 +1,17 @@ package io.quarkus.jdbc.db2.runtime; +import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.exceptionsorter.DB2ExceptionSorter; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @JdbcDriver(DatabaseKind.DB2) public class DB2AgroalConnectionConfigurer implements AgroalConnectionConfigurer { + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + dataSourceConfiguration.connectionPoolConfiguration().exceptionSorter(new DB2ExceptionSorter()); + } + } diff --git a/extensions/jdbc/jdbc-derby/deployment/pom.xml b/extensions/jdbc/jdbc-derby/deployment/pom.xml index 3b3576eff2d05b..ac40b1cea983f1 100644 --- a/extensions/jdbc/jdbc-derby/deployment/pom.xml +++ b/extensions/jdbc/jdbc-derby/deployment/pom.xml @@ -43,6 +43,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + @@ -59,6 +64,35 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + test-devservices + + + test-containers + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/extensions/jdbc/jdbc-derby/deployment/src/test/java/io/quarkus/jdbc/derby/deployment/DevServicesDerbyDatasourceTestCase.java b/extensions/jdbc/jdbc-derby/deployment/src/test/java/io/quarkus/jdbc/derby/deployment/DevServicesDerbyDatasourceTestCase.java index b57cf9cdcba683..93fcb2e2eb7a80 100644 --- a/extensions/jdbc/jdbc-derby/deployment/src/test/java/io/quarkus/jdbc/derby/deployment/DevServicesDerbyDatasourceTestCase.java +++ b/extensions/jdbc/jdbc-derby/deployment/src/test/java/io/quarkus/jdbc/derby/deployment/DevServicesDerbyDatasourceTestCase.java @@ -1,11 +1,13 @@ package io.quarkus.jdbc.derby.deployment; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; -import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.LogRecord; import javax.inject.Inject; @@ -16,18 +18,23 @@ import io.agroal.api.AgroalDataSource; import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration.ExceptionSorter; import io.quarkus.test.QuarkusUnitTest; public class DevServicesDerbyDatasourceTestCase { @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() - .setArchiveProducer(new Supplier() { - @Override - public JavaArchive get() { - return ShrinkWrap.create(JavaArchive.class); - } - }); + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty());; @Inject AgroalDataSource dataSource; @@ -44,6 +51,7 @@ public void testDatasource() throws Exception { } assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:derby:")); assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(ExceptionSorter.emptyExceptionSorter().getClass()); try (Connection connection = dataSource.getConnection()) { } diff --git a/extensions/jdbc/jdbc-derby/runtime/src/main/java/io/quarkus/jdbc/derby/runtime/DerbyAgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-derby/runtime/src/main/java/io/quarkus/jdbc/derby/runtime/DerbyAgroalConnectionConfigurer.java index 91949617f01d54..30a92fa9c1480d 100644 --- a/extensions/jdbc/jdbc-derby/runtime/src/main/java/io/quarkus/jdbc/derby/runtime/DerbyAgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-derby/runtime/src/main/java/io/quarkus/jdbc/derby/runtime/DerbyAgroalConnectionConfigurer.java @@ -1,5 +1,6 @@ package io.quarkus.jdbc.derby.runtime; +import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @@ -7,4 +8,10 @@ @JdbcDriver(DatabaseKind.DERBY) public class DerbyAgroalConnectionConfigurer implements AgroalConnectionConfigurer { + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + // Do not log a warning: we don't have an exception sorter for Derby, + // but there is nothing the user can do about it. + } + } diff --git a/extensions/jdbc/jdbc-h2/runtime/src/main/java/io/quarkus/jdbc/h2/runtime/H2AgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-h2/runtime/src/main/java/io/quarkus/jdbc/h2/runtime/H2AgroalConnectionConfigurer.java index d5553d931116df..b328ba6eec529b 100644 --- a/extensions/jdbc/jdbc-h2/runtime/src/main/java/io/quarkus/jdbc/h2/runtime/H2AgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-h2/runtime/src/main/java/io/quarkus/jdbc/h2/runtime/H2AgroalConnectionConfigurer.java @@ -12,4 +12,11 @@ public class H2AgroalConnectionConfigurer implements AgroalConnectionConfigurer public void disableSslSupport(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { // do not log anything for H2 } + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + // Do not log a warning: we don't have an exception sorter for H2, + // but there is nothing the user can do about it. + } + } diff --git a/extensions/jdbc/jdbc-mariadb/deployment/pom.xml b/extensions/jdbc/jdbc-mariadb/deployment/pom.xml index 60f430220ca285..8129318397180a 100644 --- a/extensions/jdbc/jdbc-mariadb/deployment/pom.xml +++ b/extensions/jdbc/jdbc-mariadb/deployment/pom.xml @@ -43,6 +43,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + diff --git a/extensions/jdbc/jdbc-mariadb/deployment/src/test/java/io/quarkus/jdbc/mariadb/deployment/DevServicesMariaDBDatasourceTestCase.java b/extensions/jdbc/jdbc-mariadb/deployment/src/test/java/io/quarkus/jdbc/mariadb/deployment/DevServicesMariaDBDatasourceTestCase.java index ebf9a26b1a578c..6b21bed059d8f9 100644 --- a/extensions/jdbc/jdbc-mariadb/deployment/src/test/java/io/quarkus/jdbc/mariadb/deployment/DevServicesMariaDBDatasourceTestCase.java +++ b/extensions/jdbc/jdbc-mariadb/deployment/src/test/java/io/quarkus/jdbc/mariadb/deployment/DevServicesMariaDBDatasourceTestCase.java @@ -1,11 +1,13 @@ package io.quarkus.jdbc.mariadb.deployment; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; -import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.LogRecord; import javax.inject.Inject; @@ -16,18 +18,23 @@ import io.agroal.api.AgroalDataSource; import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.exceptionsorter.MySQLExceptionSorter; import io.quarkus.test.QuarkusUnitTest; public class DevServicesMariaDBDatasourceTestCase { @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() - .setArchiveProducer(new Supplier() { - @Override - public JavaArchive get() { - return ShrinkWrap.create(JavaArchive.class); - } - }); + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); @Inject AgroalDataSource dataSource; @@ -45,6 +52,7 @@ public void testDatasource() throws Exception { assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:mariadb:")); assertEquals("quarkus", configuration.connectionFactoryConfiguration().principal().getName()); assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(MySQLExceptionSorter.class); try (Connection connection = dataSource.getConnection()) { } diff --git a/extensions/jdbc/jdbc-mariadb/runtime/src/main/java/io/quarkus/jdbc/mariadb/runtime/MariaDBAgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-mariadb/runtime/src/main/java/io/quarkus/jdbc/mariadb/runtime/MariaDBAgroalConnectionConfigurer.java index d7e1f54c11ae7f..1c3e7cfdb498b3 100644 --- a/extensions/jdbc/jdbc-mariadb/runtime/src/main/java/io/quarkus/jdbc/mariadb/runtime/MariaDBAgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-mariadb/runtime/src/main/java/io/quarkus/jdbc/mariadb/runtime/MariaDBAgroalConnectionConfigurer.java @@ -1,6 +1,7 @@ package io.quarkus.jdbc.mariadb.runtime; import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.exceptionsorter.MySQLExceptionSorter; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @@ -12,4 +13,11 @@ public class MariaDBAgroalConnectionConfigurer implements AgroalConnectionConfig public void disableSslSupport(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { dataSourceConfiguration.connectionPoolConfiguration().connectionFactoryConfiguration().jdbcProperty("useSSL", "false"); } + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + // This exception sorter is apparently valid for MariaDB too. + dataSourceConfiguration.connectionPoolConfiguration().exceptionSorter(new MySQLExceptionSorter()); + } + } diff --git a/extensions/jdbc/jdbc-mssql/deployment/pom.xml b/extensions/jdbc/jdbc-mssql/deployment/pom.xml index d156151ecaf9ee..919f95389f615d 100644 --- a/extensions/jdbc/jdbc-mssql/deployment/pom.xml +++ b/extensions/jdbc/jdbc-mssql/deployment/pom.xml @@ -33,6 +33,21 @@ io.quarkus quarkus-devservices-mssql + + io.quarkus + quarkus-agroal-deployment + test + + + io.quarkus + quarkus-junit5-internal + test + + + org.assertj + assertj-core + test + @@ -49,7 +64,35 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + test-devservices + + + test-containers + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/extensions/jdbc/jdbc-mssql/deployment/src/test/java/io/quarkus/jdbc/mssql/deployment/DevServicesMsSQLDatasourceTestCase.java b/extensions/jdbc/jdbc-mssql/deployment/src/test/java/io/quarkus/jdbc/mssql/deployment/DevServicesMsSQLDatasourceTestCase.java new file mode 100644 index 00000000000000..a0b582941aff87 --- /dev/null +++ b/extensions/jdbc/jdbc-mssql/deployment/src/test/java/io/quarkus/jdbc/mssql/deployment/DevServicesMsSQLDatasourceTestCase.java @@ -0,0 +1,53 @@ +package io.quarkus.jdbc.mssql.deployment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Connection; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.agroal.api.AgroalDataSource; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.exceptionsorter.MSSQLExceptionSorter; +import io.quarkus.test.QuarkusUnitTest; + +public class DevServicesMsSQLDatasourceTestCase { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("container-license-acceptance.txt")) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); + + @Inject + AgroalDataSource dataSource; + + @Test + public void testDatasource() throws Exception { + AgroalConnectionPoolConfiguration configuration = dataSource.getConfiguration().connectionPoolConfiguration(); + assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:sqlserver:")); + assertEquals("SA", configuration.connectionFactoryConfiguration().principal().getName()); + assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(MSSQLExceptionSorter.class); + + try (Connection connection = dataSource.getConnection()) { + } + } +} diff --git a/extensions/jdbc/jdbc-mssql/deployment/src/test/resources/container-license-acceptance.txt b/extensions/jdbc/jdbc-mssql/deployment/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000000..8a704b9c066732 --- /dev/null +++ b/extensions/jdbc/jdbc-mssql/deployment/src/test/resources/container-license-acceptance.txt @@ -0,0 +1 @@ +mcr.microsoft.com/mssql/server:2017-CU12 diff --git a/extensions/jdbc/jdbc-mssql/runtime/src/main/java/io/quarkus/jdbc/mssql/runtime/MsSQLAgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-mssql/runtime/src/main/java/io/quarkus/jdbc/mssql/runtime/MsSQLAgroalConnectionConfigurer.java index 01933adb7e7f14..63656467decd06 100644 --- a/extensions/jdbc/jdbc-mssql/runtime/src/main/java/io/quarkus/jdbc/mssql/runtime/MsSQLAgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-mssql/runtime/src/main/java/io/quarkus/jdbc/mssql/runtime/MsSQLAgroalConnectionConfigurer.java @@ -1,6 +1,7 @@ package io.quarkus.jdbc.mssql.runtime; import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.exceptionsorter.MSSQLExceptionSorter; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @@ -12,4 +13,10 @@ public class MsSQLAgroalConnectionConfigurer implements AgroalConnectionConfigur public void disableSslSupport(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { dataSourceConfiguration.connectionPoolConfiguration().connectionFactoryConfiguration().jdbcProperty("encrypt", "false"); } + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + dataSourceConfiguration.connectionPoolConfiguration().exceptionSorter(new MSSQLExceptionSorter()); + } + } diff --git a/extensions/jdbc/jdbc-mysql/deployment/pom.xml b/extensions/jdbc/jdbc-mysql/deployment/pom.xml index da17a8b1e41f19..5ed15d4a3d941b 100644 --- a/extensions/jdbc/jdbc-mysql/deployment/pom.xml +++ b/extensions/jdbc/jdbc-mysql/deployment/pom.xml @@ -43,6 +43,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + diff --git a/extensions/jdbc/jdbc-mysql/deployment/src/test/java/io/quarkus/jdbc/mysql/deployment/DevServicesMySQLDatasourceTestCase.java b/extensions/jdbc/jdbc-mysql/deployment/src/test/java/io/quarkus/jdbc/mysql/deployment/DevServicesMySQLDatasourceTestCase.java index 1a7e3bf73b07bd..1c9e9b22ae702c 100644 --- a/extensions/jdbc/jdbc-mysql/deployment/src/test/java/io/quarkus/jdbc/mysql/deployment/DevServicesMySQLDatasourceTestCase.java +++ b/extensions/jdbc/jdbc-mysql/deployment/src/test/java/io/quarkus/jdbc/mysql/deployment/DevServicesMySQLDatasourceTestCase.java @@ -1,11 +1,13 @@ package io.quarkus.jdbc.mysql.deployment; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; -import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.LogRecord; import javax.inject.Inject; @@ -16,18 +18,23 @@ import io.agroal.api.AgroalDataSource; import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.exceptionsorter.MySQLExceptionSorter; import io.quarkus.test.QuarkusUnitTest; public class DevServicesMySQLDatasourceTestCase { @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() - .setArchiveProducer(new Supplier() { - @Override - public JavaArchive get() { - return ShrinkWrap.create(JavaArchive.class); - } - }); + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); @Inject AgroalDataSource dataSource; @@ -45,6 +52,7 @@ public void testDatasource() throws Exception { assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:mysql:")); assertEquals("quarkus", configuration.connectionFactoryConfiguration().principal().getName()); assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(MySQLExceptionSorter.class); try (Connection connection = dataSource.getConnection()) { } diff --git a/extensions/jdbc/jdbc-mysql/runtime/src/main/java/io/quarkus/jdbc/mysql/runtime/MySQLAgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-mysql/runtime/src/main/java/io/quarkus/jdbc/mysql/runtime/MySQLAgroalConnectionConfigurer.java index 4910f178a2010c..6c9b5131f2f496 100644 --- a/extensions/jdbc/jdbc-mysql/runtime/src/main/java/io/quarkus/jdbc/mysql/runtime/MySQLAgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-mysql/runtime/src/main/java/io/quarkus/jdbc/mysql/runtime/MySQLAgroalConnectionConfigurer.java @@ -1,6 +1,7 @@ package io.quarkus.jdbc.mysql.runtime; import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.exceptionsorter.MySQLExceptionSorter; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @@ -12,4 +13,10 @@ public class MySQLAgroalConnectionConfigurer implements AgroalConnectionConfigur public void disableSslSupport(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { dataSourceConfiguration.connectionPoolConfiguration().connectionFactoryConfiguration().jdbcProperty("useSSL", "false"); } + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + dataSourceConfiguration.connectionPoolConfiguration().exceptionSorter(new MySQLExceptionSorter()); + } + } diff --git a/extensions/jdbc/jdbc-postgresql/deployment/pom.xml b/extensions/jdbc/jdbc-postgresql/deployment/pom.xml index 6c9f2a13cc00c7..d89e7523e1aa34 100644 --- a/extensions/jdbc/jdbc-postgresql/deployment/pom.xml +++ b/extensions/jdbc/jdbc-postgresql/deployment/pom.xml @@ -47,6 +47,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + diff --git a/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceTestCase.java b/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceTestCase.java index 948002278a19e4..e0a5765812f91a 100644 --- a/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceTestCase.java +++ b/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceTestCase.java @@ -1,11 +1,13 @@ package io.quarkus.jdbc.postgresql.deployment; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; -import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.LogRecord; import javax.inject.Inject; @@ -16,18 +18,23 @@ import io.agroal.api.AgroalDataSource; import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.api.exceptionsorter.PostgreSQLExceptionSorter; import io.quarkus.test.QuarkusUnitTest; public class DevServicesPostgresqlDatasourceTestCase { @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() - .setArchiveProducer(new Supplier() { - @Override - public JavaArchive get() { - return ShrinkWrap.create(JavaArchive.class); - } - }); + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + // Expect no warnings (in particular from Agroal) + .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue() + // There are other warnings: JDK8, TestContainers, drivers, ... + // Ignore them: we're only interested in Agroal here. + && record.getMessage().contains("Agroal")) + .assertLogRecords(records -> assertThat(records) + // This is just to get meaningful error messages, as LogRecord doesn't have a toString() + .extracting(LogRecord::getMessage) + .isEmpty()); @Inject AgroalDataSource dataSource; @@ -45,6 +52,7 @@ public void testDatasource() throws Exception { assertTrue(configuration.connectionFactoryConfiguration().jdbcUrl().contains("jdbc:postgresql:")); assertEquals("quarkus", configuration.connectionFactoryConfiguration().principal().getName()); assertEquals(20, configuration.maxSize()); + assertThat(configuration.exceptionSorter()).isInstanceOf(PostgreSQLExceptionSorter.class); try (Connection connection = dataSource.getConnection()) { } diff --git a/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSQLAgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSQLAgroalConnectionConfigurer.java index ef50570889e0a4..168adafd8aa65e 100644 --- a/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSQLAgroalConnectionConfigurer.java +++ b/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSQLAgroalConnectionConfigurer.java @@ -1,6 +1,7 @@ package io.quarkus.jdbc.postgresql.runtime; import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.exceptionsorter.PostgreSQLExceptionSorter; import io.quarkus.agroal.runtime.AgroalConnectionConfigurer; import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.datasource.common.runtime.DatabaseKind; @@ -13,4 +14,10 @@ public void disableSslSupport(String databaseKind, AgroalDataSourceConfiguration dataSourceConfiguration.connectionPoolConfiguration().connectionFactoryConfiguration().jdbcProperty("sslmode", "disable"); } + + @Override + public void setExceptionSorter(String databaseKind, AgroalDataSourceConfigurationSupplier dataSourceConfiguration) { + dataSourceConfiguration.connectionPoolConfiguration().exceptionSorter(new PostgreSQLExceptionSorter()); + } + } diff --git a/extensions/jgit/deployment/src/main/java/io/quarkus/jgit/deployment/JGitProcessor.java b/extensions/jgit/deployment/src/main/java/io/quarkus/jgit/deployment/JGitProcessor.java index 7426372e9acd84..59df930c715101 100644 --- a/extensions/jgit/deployment/src/main/java/io/quarkus/jgit/deployment/JGitProcessor.java +++ b/extensions/jgit/deployment/src/main/java/io/quarkus/jgit/deployment/JGitProcessor.java @@ -43,7 +43,10 @@ ReflectiveClassBuildItem reflection() { List runtimeInitializedClasses() { return Arrays.asList( new RuntimeInitializedClassBuildItem("org.eclipse.jgit.transport.HttpAuthMethod$Digest"), - new RuntimeInitializedClassBuildItem("org.eclipse.jgit.lib.GpgSigner")); + new RuntimeInitializedClassBuildItem("org.eclipse.jgit.lib.GpgSigner"), + // The following classes use j.u.Ramdom, so they need to be runtime-initialized + new RuntimeInitializedClassBuildItem("org.eclipse.jgit.internal.storage.file.WindowCache"), + new RuntimeInitializedClassBuildItem("org.eclipse.jgit.util.FileUtils")); } @BuildStep diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java index 77cdf9fbd5ad62..6b90114830ec72 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java @@ -46,6 +46,7 @@ import org.jboss.jandex.DotName; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; +import org.xerial.snappy.OSInfo; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -62,6 +63,7 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.pkg.NativeConfig; import io.quarkus.kafka.client.runtime.KafkaBindingConverter; @@ -198,7 +200,7 @@ private void handleSnappy(BuildProducer reflectiveClas String path = root + dir + "/" + snappyNativeLibraryName; nativeLibs.produce(new NativeImageResourceBuildItem(path)); } else { // otherwise the native lib of the platform this build runs on - String dir = getOs() + "/" + getArch(); + String dir = OSInfo.getNativeLibFolderPathForCurrentOS(); String snappyNativeLibraryName = System.mapLibraryName("snappyjava"); if (snappyNativeLibraryName.toLowerCase().endsWith(".dylib")) { snappyNativeLibraryName = snappyNativeLibraryName.replace(".dylib", ".jnilib"); @@ -395,6 +397,15 @@ UnremovableBeanBuildItem ensureJsonParserAvailable() { "javax.json.bind.Jsonb"); } + @BuildStep + public void registerRuntimeInitializedClasses(BuildProducer producer) { + // Classes using java.util.Random, which need to be runtime initialized + producer.produce( + new RuntimeInitializedClassBuildItem("org.apache.kafka.common.security.authenticator.SaslClientAuthenticator")); + producer.produce(new RuntimeInitializedClassBuildItem( + "org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin")); + } + @BuildStep void registerServiceBinding(Capabilities capabilities, BuildProducer serviceProvider) { @@ -404,23 +415,4 @@ void registerServiceBinding(Capabilities capabilities, KafkaBindingConverter.class.getName())); } } - - public static String getArch() { - String osArch = System.getProperty("os.arch"); - return osArch.replaceAll("\\W", ""); - } - - static String getOs() { - String osName = System.getProperty("os.name"); - - if (osName.contains("Windows")) { - return "Windows"; - } else if (osName.contains("Mac")) { - return "Mac"; - } else if (osName.contains("Linux")) { - return "Linux"; - } else { - return osName.replaceAll("\\W", ""); - } - } } diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaBindingConverter.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaBindingConverter.java index dbbf3bd4bbec42..f99a5b6a71082e 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaBindingConverter.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaBindingConverter.java @@ -21,7 +21,7 @@ public Optional convert(List service Map properties = new HashMap<>(); ServiceBinding binding = matchingByType.get(); - String bootstrapServers = binding.getProperties().get("bootstrapServers"); + String bootstrapServers = binding.getProperties().get("bootstrapservers"); if (bootstrapServers == null) { bootstrapServers = binding.getProperties().get("bootstrap-servers"); } @@ -42,7 +42,9 @@ public Optional convert(List service String password = binding.getProperties().get("password"); if ("PLAIN".equals(saslMechanism) && (user != null) && (password != null)) { properties.put("kafka.sasl.jaas.config", - String.format("org.apache.kafka.common.security.plain.PlainLoginModule required %s %s", user, password)); + String.format( + "org.apache.kafka.common.security.plain.PlainLoginModule required username='%s' password='%s';", + user, password)); } return Optional.of(new ServiceBindingConfigSource("kafka-k8s-service-binding-source", properties)); diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java index bdd892cd71b141..a61a496280f5f3 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java @@ -23,7 +23,7 @@ public class KafkaRuntimeConfigProducer { @Produces @DefaultBean @Singleton - @Named("default-kafka-broker") + @Named("default-kafka-broker") // TODO Should use @Identifier soon public Map createKafkaRuntimeConfig(Config config, ApplicationConfig app) { Map result = new HashMap<>(); diff --git a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java index 0da408c03c29d9..01920dd6fc3776 100644 --- a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java +++ b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java @@ -5,8 +5,10 @@ import java.net.InetSocketAddress; import java.time.Duration; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; @@ -24,6 +26,7 @@ import javax.enterprise.inject.Instance; import javax.enterprise.inject.Produces; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.apache.kafka.clients.CommonClientConfigs; @@ -64,9 +67,12 @@ public class KafkaStreamsProducer { private final KafkaStreamsTopologyManager kafkaStreamsTopologyManager; private final Admin kafkaAdminClient; + // TODO Replace @Named with @Identifier when it will be integrated + @Inject public KafkaStreamsProducer(KafkaStreamsSupport kafkaStreamsSupport, KafkaStreamsRuntimeConfig runtimeConfig, Instance topology, Instance kafkaClientSupplier, + @Named("default-kafka-broker") Instance> defaultConfiguration, Instance stateListener, Instance globalStateRestoreListener) { shutdown = false; // No producer for Topology -> nothing to do @@ -87,7 +93,12 @@ public KafkaStreamsProducer(KafkaStreamsSupport kafkaStreamsSupport, KafkaStream bootstrapServersConfig = ConfigProvider.getConfig().getOptionalValue("kafka.bootstrap.servers", String.class) .orElse(bootstrapServersConfig); } - Properties kafkaStreamsProperties = getStreamsProperties(buildTimeProperties, bootstrapServersConfig, runtimeConfig); + Map cfg = Collections.emptyMap(); + if (!defaultConfiguration.isUnsatisfied()) { + cfg = defaultConfiguration.get(); + } + Properties kafkaStreamsProperties = getStreamsProperties(buildTimeProperties, cfg, bootstrapServersConfig, + runtimeConfig); this.kafkaAdminClient = Admin.create(getAdminClientConfig(kafkaStreamsProperties)); this.executorService = Executors.newSingleThreadExecutor(); @@ -176,13 +187,17 @@ public void run() { /** * Returns all properties to be passed to Kafka Streams. */ - private static Properties getStreamsProperties(Properties properties, String bootstrapServersConfig, + private static Properties getStreamsProperties(Properties properties, + Map cfg, String bootstrapServersConfig, KafkaStreamsRuntimeConfig runtimeConfig) { Properties streamsProperties = new Properties(); // build-time options streamsProperties.putAll(properties); + // default configuration + streamsProperties.putAll(cfg); + // dynamic add -- back-compatibility streamsProperties.putAll(KafkaStreamsPropertiesUtil.quarkusKafkaStreamsProperties()); streamsProperties.putAll(KafkaStreamsPropertiesUtil.appKafkaStreamsProperties()); diff --git a/extensions/keycloak-authorization/deployment/pom.xml b/extensions/keycloak-authorization/deployment/pom.xml index 4425aef6ad5f49..10aa8f76f8fcb5 100644 --- a/extensions/keycloak-authorization/deployment/pom.xml +++ b/extensions/keycloak-authorization/deployment/pom.xml @@ -20,6 +20,10 @@ io.quarkus quarkus-oidc-deployment + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-keycloak-authorization diff --git a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java index b7d852a95d5c05..070128488a6894 100644 --- a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java +++ b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java @@ -2,8 +2,10 @@ import java.util.Map; +import javax.inject.Singleton; + import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -12,6 +14,7 @@ import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerAuthorizer; import io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerConfig; +import io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerConfigBean; import io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerRecorder; import io.quarkus.oidc.runtime.OidcBuildTimeConfig; import io.quarkus.oidc.runtime.OidcConfig; @@ -27,8 +30,8 @@ FeatureBuildItem featureBuildItem() { } @BuildStep - RequireBodyHandlerBuildItem requireBody(KeycloakPolicyEnforcerConfig config) { - if (config.policyEnforcer.enable) { + RequireBodyHandlerBuildItem requireBody(OidcBuildTimeConfig oidcBuildTimeConfig, KeycloakPolicyEnforcerConfig config) { + if (oidcBuildTimeConfig.enabled && config.policyEnforcer.enable) { if (isBodyClaimInformationPointDefined(config.policyEnforcer.claimInformationPoint.simpleConfig)) { return new RequireBodyHandlerBuildItem(); } @@ -57,8 +60,8 @@ private boolean isBodyClaimInformationPointDefined(Map + + io.quarkus + quarkus-apache-httpclient + org.jboss.logging commons-logging-jboss-logging diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java index e3b14e7787cc49..53ed11f87e7a1c 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java @@ -8,6 +8,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import javax.inject.Inject; import javax.inject.Singleton; import org.keycloak.AuthorizationContext; @@ -17,13 +19,11 @@ import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; +import io.quarkus.oidc.AccessTokenCredential; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.common.runtime.OidcCommonConfig.Tls.Verification; -import io.quarkus.oidc.runtime.OidcConfig; -import io.quarkus.runtime.TlsConfig; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.runtime.QuarkusSecurityIdentity; -import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @@ -32,8 +32,9 @@ public class KeycloakPolicyEnforcerAuthorizer implements HttpSecurityPolicy, BiFunction { - private volatile KeycloakAdapterPolicyEnforcer delegate; - private volatile long readTimeout; + @Inject + KeycloakPolicyEnforcerConfigBean configBean; + private KeycloakAdapterPolicyEnforcer delegate; @Override public Uni checkPermission(RoutingContext request, Uni identity, @@ -43,7 +44,18 @@ public Uni checkPermission(RoutingContext request, Uni apply(Permission permission) { }).build(); } - public void init(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, TlsConfig tlsConfig, - HttpConfiguration httpConfiguration) { + @PostConstruct + public void init() { AdapterConfig adapterConfig = new AdapterConfig(); - String authServerUrl = oidcConfig.defaultTenant.getAuthServerUrl().get(); + String authServerUrl = configBean.oidcConfig.defaultTenant.getAuthServerUrl().get(); try { adapterConfig.setRealm(authServerUrl.substring(authServerUrl.lastIndexOf('/') + 1)); @@ -103,22 +115,24 @@ public void init(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, Tls throw new RuntimeException("Failed to parse the realm name.", cause); } - adapterConfig.setResource(oidcConfig.defaultTenant.getClientId().get()); - adapterConfig.setCredentials(getCredentials(oidcConfig.defaultTenant)); + adapterConfig.setResource(configBean.oidcConfig.defaultTenant.getClientId().get()); + adapterConfig.setCredentials(getCredentials(configBean.oidcConfig.defaultTenant)); - boolean trustAll = oidcConfig.defaultTenant.tls.getVerification().isPresent() - ? oidcConfig.defaultTenant.tls.getVerification().get() == Verification.NONE - : tlsConfig.trustAll; + boolean trustAll = configBean.oidcConfig.defaultTenant.tls.getVerification().isPresent() + ? configBean.oidcConfig.defaultTenant.tls.getVerification().get() == Verification.NONE + : configBean.tlsConfig.trustAll; if (trustAll) { adapterConfig.setDisableTrustManager(true); adapterConfig.setAllowAnyHostname(true); } - if (oidcConfig.defaultTenant.proxy.host.isPresent()) { - adapterConfig.setProxyUrl(oidcConfig.defaultTenant.proxy.host.get() + ":" + oidcConfig.defaultTenant.proxy.port); + if (configBean.oidcConfig.defaultTenant.proxy.host.isPresent()) { + adapterConfig.setProxyUrl(configBean.oidcConfig.defaultTenant.proxy.host.get() + ":" + + configBean.oidcConfig.defaultTenant.proxy.port); } - PolicyEnforcerConfig enforcerConfig = getPolicyEnforcerConfig(config, adapterConfig); + PolicyEnforcerConfig enforcerConfig = getPolicyEnforcerConfig(configBean.keycloakPolicyEnforcerConfig, + adapterConfig); if (enforcerConfig == null) { return; @@ -126,7 +140,6 @@ public void init(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, Tls adapterConfig.setPolicyEnforcerConfig(enforcerConfig); - this.readTimeout = httpConfiguration.readTimeout.toMillis(); this.delegate = new KeycloakAdapterPolicyEnforcer( new PolicyEnforcer(KeycloakDeploymentBuilder.build(adapterConfig), adapterConfig)); } diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfigBean.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfigBean.java new file mode 100644 index 00000000000000..0dbb1217c6239a --- /dev/null +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfigBean.java @@ -0,0 +1,22 @@ +package io.quarkus.keycloak.pep.runtime; + +import io.quarkus.oidc.runtime.OidcConfig; +import io.quarkus.runtime.TlsConfig; +import io.quarkus.vertx.http.runtime.HttpConfiguration; + +public class KeycloakPolicyEnforcerConfigBean { + + final OidcConfig oidcConfig; + final KeycloakPolicyEnforcerConfig keycloakPolicyEnforcerConfig; + final TlsConfig tlsConfig; + final HttpConfiguration httpConfiguration; + + public KeycloakPolicyEnforcerConfigBean(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig keycloakPolicyEnforcerConfig, + TlsConfig tlsConfig, + HttpConfiguration httpConfiguration) { + this.oidcConfig = oidcConfig; + this.keycloakPolicyEnforcerConfig = keycloakPolicyEnforcerConfig; + this.tlsConfig = tlsConfig; + this.httpConfiguration = httpConfiguration; + } +} diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java index 23037ec5bc072f..712995e8015a19 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java @@ -1,6 +1,7 @@ package io.quarkus.keycloak.pep.runtime; -import io.quarkus.arc.runtime.BeanContainer; +import java.util.function.Supplier; + import io.quarkus.oidc.OIDCException; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.runtime.OidcConfig; @@ -11,12 +12,16 @@ @Recorder public class KeycloakPolicyEnforcerRecorder { - public void setup(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, TlsConfig tlsConfig, - BeanContainer beanContainer, - HttpConfiguration httpConfiguration) { + public Supplier setup(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, + TlsConfig tlsConfig, HttpConfiguration httpConfiguration) { if (oidcConfig.defaultTenant.applicationType == OidcTenantConfig.ApplicationType.WEB_APP) { throw new OIDCException("Application type [" + oidcConfig.defaultTenant.applicationType + "] is not supported"); } - beanContainer.instance(KeycloakPolicyEnforcerAuthorizer.class).init(oidcConfig, config, tlsConfig, httpConfiguration); + return new Supplier() { + @Override + public KeycloakPolicyEnforcerConfigBean get() { + return new KeycloakPolicyEnforcerConfigBean(oidcConfig, config, tlsConfig, httpConfiguration); + } + }; } } diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java index 19eeb9922df5c8..ea02e3e5b65a40 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java @@ -19,11 +19,7 @@ import org.keycloak.representations.AccessToken; import io.netty.handler.codec.http.HttpHeaderNames; -import io.quarkus.oidc.AccessTokenCredential; -import io.quarkus.security.credential.TokenCredential; -import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.vertx.http.runtime.VertxInputStream; -import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; @@ -35,13 +31,15 @@ public class VertxHttpFacade implements OIDCHttpFacade { private final Response response; private final RoutingContext routingContext; private final Request request; + private final String token; private final long readTimeout; - public VertxHttpFacade(RoutingContext routingContext, long readTimeout) { + public VertxHttpFacade(RoutingContext routingContext, String token, long readTimeout) { this.routingContext = routingContext; + this.token = token; this.readTimeout = readTimeout; - request = createRequest(routingContext); - response = createResponse(routingContext); + this.request = createRequest(routingContext); + this.response = createResponse(routingContext); } @Override @@ -222,18 +220,6 @@ public void end() { @Override public KeycloakSecurityContext getSecurityContext() { - SecurityIdentity identity = QuarkusHttpUser.getSecurityIdentityBlocking(routingContext, null); - if (identity == null) { - return null; - } - TokenCredential credential = identity.getCredential(AccessTokenCredential.class); - - if (credential == null) { - return null; - } - - String token = credential.getToken(); - try { return new KeycloakSecurityContext(token, new JWSInput(token).readJsonContent(AccessToken.class), null, null); } catch (JWSInputException e) { diff --git a/extensions/kotlin/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/kotlin/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 9671121f1d978f..04d3b298991674 100644 --- a/extensions/kotlin/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/kotlin/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -9,4 +9,4 @@ metadata: codestart: name: "kotlin" kind: "core" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/kubernetes-client/deployment-internal/pom.xml b/extensions/kubernetes-client/deployment-internal/pom.xml index 0bbcb33d6643fd..5e16758ca8d8c3 100644 --- a/extensions/kubernetes-client/deployment-internal/pom.xml +++ b/extensions/kubernetes-client/deployment-internal/pom.xml @@ -9,8 +9,8 @@ 4.0.0 - quarkus-kubernetes-client-deployment-internal - Quarkus - Kubernetes Client - Deployment - Internal + quarkus-kubernetes-client-internal-deployment + Quarkus - Kubernetes Client - Internal - Deployment This module only exists as a separate module to so the kubernetes extension can share code with the kubernetes-client extension diff --git a/extensions/kubernetes-client/deployment/pom.xml b/extensions/kubernetes-client/deployment/pom.xml index d05dfe39a70255..0cc8786ff156d6 100644 --- a/extensions/kubernetes-client/deployment/pom.xml +++ b/extensions/kubernetes-client/deployment/pom.xml @@ -19,7 +19,7 @@ io.quarkus - quarkus-kubernetes-client-deployment-internal + quarkus-kubernetes-client-internal-deployment io.quarkus diff --git a/extensions/kubernetes-client/runtime-internal/pom.xml b/extensions/kubernetes-client/runtime-internal/pom.xml index 104fdb101e44cd..2735ea38b3be90 100644 --- a/extensions/kubernetes-client/runtime-internal/pom.xml +++ b/extensions/kubernetes-client/runtime-internal/pom.xml @@ -28,6 +28,10 @@ + + io.quarkus + quarkus-bootstrap-maven-plugin + maven-compiler-plugin diff --git a/extensions/kubernetes-client/runtime-internal/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/kubernetes-client/runtime-internal/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..4ea25b879def3c --- /dev/null +++ b/extensions/kubernetes-client/runtime-internal/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +--- +name: "Kubernetes Client Internal" +metadata: + keywords: + - "kubernetes-client-internal" + categories: + - "cloud" + status: "stable" + unlisted: true diff --git a/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java b/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java index a4d541c5bb9a6e..9c0bba4c82e577 100644 --- a/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java +++ b/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java @@ -35,6 +35,7 @@ import io.quarkus.deployment.pkg.PackageConfig; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.kubernetes.deployment.AddNodePortDecorator; +import io.quarkus.kubernetes.deployment.AddPortToKubernetesConfig; import io.quarkus.kubernetes.deployment.ApplyContainerImageDecorator; import io.quarkus.kubernetes.deployment.ApplyHttpGetActionPortDecorator; import io.quarkus.kubernetes.deployment.ApplyServiceTypeDecorator; @@ -82,12 +83,13 @@ public void createLabels(KubernetesConfig config, BuildProducer createConfigurators(KubernetesConfig config, List ports) { + public List createConfigurators(KubernetesConfig config, + List ports) { List result = new ArrayList<>(); - result.addAll(KubernetesCommonHelper.createPlatformConfigurators(config)); - result.addAll(KubernetesCommonHelper.createGlobalConfigurators(ports)); + KubernetesCommonHelper.combinePorts(ports, config).entrySet().forEach(e -> { + result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(e.getValue()))); + }); return result; - } @BuildStep diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/ConfiguratorBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/ConfiguratorBuildItem.java index 8eb01d10b9645d..09152b2d537b71 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/ConfiguratorBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/ConfiguratorBuildItem.java @@ -7,7 +7,7 @@ /** * A build item that wraps around Configurator objects. * The purpose of those build items is influence the configuration that will be feed to the generator process. - * Configurators are similar to decorators, but are applied to configuration instead of generated resources. + * ConfigurationRegistry are similar to decorators, but are applied to configuration instead of generated resources. */ public final class ConfiguratorBuildItem extends MultiBuildItem { diff --git a/extensions/kubernetes/vanilla/deployment/pom.xml b/extensions/kubernetes/vanilla/deployment/pom.xml index 73a0f7b24f3c24..3898ab1f2e1537 100644 --- a/extensions/kubernetes/vanilla/deployment/pom.xml +++ b/extensions/kubernetes/vanilla/deployment/pom.xml @@ -27,7 +27,7 @@ io.quarkus - quarkus-kubernetes-client-deployment-internal + quarkus-kubernetes-client-internal-deployment io.dekorate diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNamespaceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNamespaceDecorator.java index ddf9433fc2c4fd..0733592f0b6240 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNamespaceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNamespaceDecorator.java @@ -2,12 +2,12 @@ import java.util.Objects; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.kubernetes.api.model.ObjectMetaBuilder; import io.dekorate.kubernetes.decorator.AddSidecarDecorator; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; public class AddNamespaceDecorator extends NamedResourceDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNodePortDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNodePortDecorator.java index c3c4f9da2c87ad..045b7419a0bdba 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNodePortDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddNodePortDecorator.java @@ -4,10 +4,10 @@ import org.jboss.logging.Logger; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.kubernetes.api.model.ServiceSpecFluent; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ServiceSpecFluent; public class AddNodePortDecorator extends NamedResourceDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java new file mode 100644 index 00000000000000..4b8e18dd71fc9d --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java @@ -0,0 +1,54 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Objects; + +import io.dekorate.knative.config.KnativeConfigFluent; +import io.dekorate.kubernetes.config.Configurator; +import io.dekorate.kubernetes.config.Port; + +public class AddPortToKnativeConfig extends Configurator> { + + private final Port port; + + public AddPortToKnativeConfig(Port port) { + this.port = port; + } + + @Override + public void visit(KnativeConfigFluent config) { + if (!hasPort(config)) { + config.addToPorts(port); + } + } + + /** + * Check if the {@link KnativeConfig} already has port. + * + * @param config The port. + * @return True if port with same container port exists. + */ + private boolean hasPort(KnativeConfigFluent config) { + for (Port p : config.getPorts()) { + if (p.getContainerPort() == port.getContainerPort()) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + AddPortToKnativeConfig addPort = (AddPortToKnativeConfig) o; + return Objects.equals(port, addPort.port); + } + + @Override + public int hashCode() { + + return Objects.hash(port); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java new file mode 100644 index 00000000000000..f5a2b6167f36b5 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java @@ -0,0 +1,54 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Objects; + +import io.dekorate.kubernetes.config.Configurator; +import io.dekorate.kubernetes.config.KubernetesConfigFluent; +import io.dekorate.kubernetes.config.Port; + +public class AddPortToKubernetesConfig extends Configurator> { + + private final Port port; + + public AddPortToKubernetesConfig(Port port) { + this.port = port; + } + + @Override + public void visit(KubernetesConfigFluent config) { + if (!hasPort(config)) { + config.addToPorts(port); + } + } + + /** + * Check if the {@link KubernetesConfig} already has port. + * + * @param config The port. + * @return True if port with same container port exists. + */ + private boolean hasPort(KubernetesConfigFluent config) { + for (Port p : config.getPorts()) { + if (p.getContainerPort() == port.getContainerPort()) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + AddPortToKubernetesConfig addPort = (AddPortToKubernetesConfig) o; + return Objects.equals(port, addPort.port); + } + + @Override + public int hashCode() { + + return Objects.hash(port); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java new file mode 100644 index 00000000000000..fd47b27b2cc230 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java @@ -0,0 +1,54 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Objects; + +import io.dekorate.kubernetes.config.Configurator; +import io.dekorate.kubernetes.config.Port; +import io.dekorate.openshift.config.OpenshiftConfigFluent; + +public class AddPortToOpenshiftConfig extends Configurator> { + + private final Port port; + + public AddPortToOpenshiftConfig(Port port) { + this.port = port; + } + + @Override + public void visit(OpenshiftConfigFluent config) { + if (!hasPort(config)) { + config.addToPorts(port); + } + } + + /** + * Check if the {@link OpenshiftConfig} already has port. + * + * @param config The port. + * @return True if port with same container port exists. + */ + private boolean hasPort(OpenshiftConfigFluent config) { + for (Port p : config.getPorts()) { + if (p.getContainerPort() == port.getContainerPort()) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + AddPortToOpenshiftConfig addPort = (AddPortToOpenshiftConfig) o; + return Objects.equals(port, addPort.port); + } + + @Override + public int hashCode() { + + return Objects.hash(port); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleResourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleResourceDecorator.java index f173102360c783..2ed8bac98d2210 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleResourceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleResourceDecorator.java @@ -2,11 +2,11 @@ import java.util.stream.Collectors; -import io.dekorate.deps.kubernetes.api.model.KubernetesListBuilder; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.kubernetes.api.model.rbac.PolicyRuleBuilder; -import io.dekorate.deps.kubernetes.api.model.rbac.RoleBuilder; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder; +import io.fabric8.kubernetes.api.model.rbac.RoleBuilder; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; class AddRoleResourceDecorator extends ResourceProvidingDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddSidecarDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddSidecarDecorator.java index 8db4519a8048f7..b7d9ea8c3b757b 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddSidecarDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddSidecarDecorator.java @@ -1,11 +1,11 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.kubernetes.api.model.PodSpecBuilder; import io.dekorate.kubernetes.config.Container; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.PodSpecBuilder; /** * Copied from dekorate in order to fix some issues diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyBuilderImageDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyBuilderImageDecorator.java index eb82a153a03086..5afcc842c524a0 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyBuilderImageDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyBuilderImageDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.openshift.api.model.SourceBuildStrategyFluent; import io.dekorate.kubernetes.decorator.*; import io.dekorate.s2i.decorator.AddBuildConfigResourceDecorator; import io.dekorate.utils.Images; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.openshift.api.model.SourceBuildStrategyFluent; public class ApplyBuilderImageDecorator extends NamedResourceDecorator> { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyContainerImageDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyContainerImageDecorator.java index 0ddb80e02dd93d..10445b325e04f2 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyContainerImageDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyContainerImageDecorator.java @@ -1,12 +1,12 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ContainerFluent; import io.dekorate.kubernetes.decorator.AddInitContainerDecorator; import io.dekorate.kubernetes.decorator.AddSidecarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.ApplyImageDecorator; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ContainerFluent; /** * A decorator for applying an image to a container capable of overriding the internal {@link ApplyImageDecorator}. diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyHttpGetActionPortDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyHttpGetActionPortDecorator.java index 45b306d5b2b389..9722e7d4e923ca 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyHttpGetActionPortDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyHttpGetActionPortDecorator.java @@ -1,13 +1,13 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.HTTPGetActionFluent; import io.dekorate.kubernetes.decorator.AddLivenessProbeDecorator; import io.dekorate.kubernetes.decorator.AddReadinessProbeDecorator; import io.dekorate.kubernetes.decorator.AddSidecarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.HTTPGetActionFluent; public class ApplyHttpGetActionPortDecorator extends ApplicationContainerDecorator> { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyServiceTypeDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyServiceTypeDecorator.java index 5c40c434a16a73..9fc2d3e5ccb730 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyServiceTypeDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ApplyServiceTypeDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.kubernetes.api.model.ServiceSpecFluent; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ServiceSpecFluent; /** * A decorator for applying a serviceType to the container diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerAdapter.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerAdapter.java index 70b254d16881c3..d736beb7a2df60 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerAdapter.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerAdapter.java @@ -1,7 +1,5 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.Container; -import io.dekorate.deps.kubernetes.api.model.ContainerBuilder; import io.dekorate.kubernetes.config.Env; import io.dekorate.kubernetes.config.Mount; import io.dekorate.kubernetes.config.Port; @@ -13,6 +11,8 @@ import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; import io.dekorate.utils.Images; import io.dekorate.utils.Strings; +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ContainerBuilder; /** * Copied from dekorate in order to fix some issues diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java index dc94d5dd856bbb..63f3b29375392c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java @@ -138,7 +138,7 @@ public String getTargetPlatformName() { public Collection convertToEnvs() { return convertToBuildItems().stream() .map(kebi -> new Env(EnvConverter.convertName(kebi.getName()), kebi.getValue(), kebi.getSecret(), - kebi.getConfigMap(), kebi.getField())) + kebi.getConfigMap(), kebi.getField(), null)) .collect(Collectors.toList()); } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java index 7609d7d0ceeeb5..41b25955aeb125 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java @@ -90,10 +90,10 @@ public void createLabels(KnativeConfig config, BuildProducer createConfigurators(KnativeConfig config, List ports) { List result = new ArrayList<>(); - result.addAll(KubernetesCommonHelper.createPlatformConfigurators(config)); - result.addAll(KubernetesCommonHelper.createGlobalConfigurators(ports)); + KubernetesCommonHelper.combinePorts(ports, config).entrySet().forEach(e -> { + result.add(new ConfiguratorBuildItem(new AddPortToKnativeConfig(e.getValue()))); + }); return result; - } @BuildStep diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java index 07dadc7b8d506a..241498d9a686fc 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java @@ -23,9 +23,9 @@ import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder; import io.dekorate.kubernetes.config.EnvBuilder; import io.dekorate.kubernetes.config.MountBuilder; +import io.dekorate.kubernetes.config.Port; import io.dekorate.kubernetes.config.PortBuilder; import io.dekorate.kubernetes.config.SecretVolumeBuilder; -import io.dekorate.kubernetes.configurator.AddPort; import io.dekorate.kubernetes.decorator.AddAnnotationDecorator; import io.dekorate.kubernetes.decorator.AddAwsElasticBlockStoreVolumeDecorator; import io.dekorate.kubernetes.decorator.AddAzureDiskVolumeDecorator; @@ -58,11 +58,11 @@ import io.dekorate.project.Project; import io.dekorate.project.ScmInfo; import io.dekorate.utils.Annotations; +import io.dekorate.utils.Strings; import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.pkg.PackageConfig; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; -import io.quarkus.kubernetes.spi.ConfiguratorBuildItem; import io.quarkus.kubernetes.spi.DecoratorBuildItem; import io.quarkus.kubernetes.spi.KubernetesAnnotationBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; @@ -102,23 +102,35 @@ public static Optional createProject(ApplicationInfoBuildItem app, Path } /** - * Creates the common configurator build items. + * Creates the configurator build items. */ - public static List createGlobalConfigurators(List ports) { - List result = new ArrayList<>(); - verifyPorts(ports).entrySet().stream() + public static Map combinePorts(List ports, + PlatformConfiguration config) { + Map allPorts = new HashMap<>(); + allPorts.putAll(verifyPorts(ports).entrySet().stream() .map(e -> new PortBuilder().withName(e.getKey()).withContainerPort(e.getValue()).build()) - .forEach(p -> result.add(new ConfiguratorBuildItem(new AddPort(p)))); - return result; - } - - /** - * Creates the common configurator build items. - */ - public static List createPlatformConfigurators(PlatformConfiguration config) { - List result = new ArrayList<>(); - config.getPorts().entrySet().forEach(e -> result.add(new ConfiguratorBuildItem(new AddPort(PortConverter.convert(e))))); - return result; + .collect(Collectors.toMap(Port::getName, p -> p))); + + config.getPorts().entrySet().forEach(e -> { + String name = e.getKey(); + Port configuredPort = PortConverter.convert(e); + Port buildItemPort = allPorts.get(name); + Port combinedPort = buildItemPort == null ? configuredPort + : new PortBuilder() + .withName(name) + .withHostPort(configuredPort.getHostPort() != null && configuredPort.getHostPort() != 0 + ? configuredPort.getHostPort() + : buildItemPort.getHostPort()) + .withContainerPort( + configuredPort.getContainerPort() != null && configuredPort.getContainerPort() != 0 + ? configuredPort.getContainerPort() + : buildItemPort.getContainerPort()) + .withPath(Strings.isNotNullOrEmpty(configuredPort.getPath()) ? configuredPort.getPath() + : buildItemPort.getPath()) + .build(); + allPorts.put(name, combinedPort); + }); + return allPorts; } /** @@ -341,7 +353,7 @@ private static List createAnnotationDecorators(Optional { ScmInfo scm = p.getScmInfo(); - String vcsUrl = scm != null ? scm.getUrl() : null; + String vcsUrl = scm != null ? scm.getRemote().get("origin") : null; String commitId = scm != null ? scm.getCommit() : null; //Dekorate uses its own annotations. Let's replace them with the quarkus ones. 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 59f0bc43ac1698..b74b6df3f3f6c9 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 @@ -21,14 +21,14 @@ import org.jboss.logging.Logger; -import io.dekorate.deps.kubernetes.api.model.HasMetadata; -import io.dekorate.deps.kubernetes.api.model.KubernetesList; -import io.dekorate.deps.kubernetes.client.KubernetesClient; -import io.dekorate.deps.kubernetes.client.KubernetesClientException; -import io.dekorate.deps.openshift.api.model.Route; -import io.dekorate.deps.openshift.client.OpenShiftClient; import io.dekorate.utils.Clients; import io.dekorate.utils.Serialization; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.client.OpenShiftClient; import io.quarkus.container.image.deployment.ContainerImageCapabilitiesUtil; import io.quarkus.container.spi.ContainerImageInfoBuildItem; import io.quarkus.deployment.Capabilities; diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java index c1c3bbf1e90eaf..934dd41cc7dabb 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java @@ -91,7 +91,7 @@ public void build(ApplicationInfoBuildItem applicationInfo, List decorators, BuildProducer generatedResourceProducer) { - List allConfigurators = new ArrayList<>(configurators); + List allConfigurationRegistry = new ArrayList<>(configurators); List allDecorators = new ArrayList<>(decorators); if (kubernetesPorts.isEmpty()) { @@ -137,13 +137,13 @@ public void build(ApplicationInfoBuildItem applicationInfo, session.setWriter(sessionWriter); session.setReader(sessionReader); - session.feed(Maps.fromProperties(config)); + session.addPropertyConfiguration(Maps.fromProperties(config)); //We need to verify to filter out anything that doesn't extend the Configurator class. //The ConfiguratorBuildItem is a wrapper to Object. - allConfigurators.stream().filter(d -> d.matches(Configurator.class)).forEach(i -> { + allConfigurationRegistry.stream().filter(d -> d.matches(Configurator.class)).forEach(i -> { Configurator configurator = (Configurator) i.getConfigurator(); - session.configurators().add(configurator); + session.getConfigurationRegistry().add(configurator); }); //We need to verify to filter out anything that doesn't extend the Decorator class. @@ -152,9 +152,9 @@ public void build(ApplicationInfoBuildItem applicationInfo, String group = i.getGroup(); Decorator decorator = (Decorator) i.getDecorator(); if (Strings.isNullOrEmpty(group)) { - session.resources().decorate(decorator); + session.getResourceRegistry().decorate(decorator); } else { - session.resources().decorate(group, decorator); + session.getResourceRegistry().decorate(group, decorator); } }); diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeHandler.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java similarity index 76% rename from extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeHandler.java rename to extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java index e185107b414585..25dad22d7068c2 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeHandler.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java @@ -4,23 +4,12 @@ import java.util.Optional; -import io.dekorate.AbstractKubernetesHandler; +import io.dekorate.AbstractKubernetesManifestGenerator; import io.dekorate.BuildServiceFactories; -import io.dekorate.Configurators; -import io.dekorate.Handler; -import io.dekorate.HandlerFactory; -import io.dekorate.Resources; +import io.dekorate.ConfigurationRegistry; +import io.dekorate.ResourceRegistry; import io.dekorate.WithProject; import io.dekorate.config.ConfigurationSupplier; -import io.dekorate.deps.kubernetes.api.model.KubernetesListBuilder; -import io.dekorate.deps.kubernetes.api.model.LabelSelector; -import io.dekorate.deps.kubernetes.api.model.LabelSelectorBuilder; -import io.dekorate.deps.kubernetes.api.model.PodSpec; -import io.dekorate.deps.kubernetes.api.model.PodSpecBuilder; -import io.dekorate.deps.kubernetes.api.model.PodTemplateSpec; -import io.dekorate.deps.kubernetes.api.model.PodTemplateSpecBuilder; -import io.dekorate.deps.kubernetes.api.model.apps.Deployment; -import io.dekorate.deps.kubernetes.api.model.apps.DeploymentBuilder; import io.dekorate.kubernetes.config.Configuration; import io.dekorate.kubernetes.config.Container; import io.dekorate.kubernetes.config.EditableKubernetesConfig; @@ -40,31 +29,37 @@ import io.dekorate.utils.Images; import io.dekorate.utils.Labels; import io.dekorate.utils.Strings; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.LabelSelector; +import io.fabric8.kubernetes.api.model.LabelSelectorBuilder; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodSpecBuilder; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; // copied from KubernetesHandler // TODO: update dekorate to make KubernetesHandler extendable -public class MinikubeHandler extends AbstractKubernetesHandler implements HandlerFactory, WithProject { +public class MinikubeManifestGenerator extends AbstractKubernetesManifestGenerator implements WithProject { private static final String DEFAULT_REGISTRY = "docker.io"; private static final String IF_NOT_PRESENT = "IfNotPresent"; private static final String KUBERNETES_NAMESPACE = "KUBERNETES_NAMESPACE"; private static final String METADATA_NAMESPACE = "metadata.namespace"; + private static final String MINIKUBE = "minikube"; - private final Configurators configurators; + private final ConfigurationRegistry configurationRegistry; - public MinikubeHandler() { - this(new Resources(), new Configurators()); - } - - public MinikubeHandler(Resources resources, Configurators configurators) { - super(resources); - this.configurators = configurators; + public MinikubeManifestGenerator(ResourceRegistry resourceRegistry, ConfigurationRegistry configurationRegistry) { + super(resourceRegistry); + this.configurationRegistry = configurationRegistry; } @Override - public Handler create(Resources resources, Configurators configurators) { - return new MinikubeHandler(resources, configurators); + public String getKey() { + return MINIKUBE; } @Override @@ -72,10 +67,10 @@ public int order() { return 210; } - public void handle(KubernetesConfig config) { - ImageConfiguration imageConfig = getImageConfiguration(getProject(), config, configurators); + public void generate(KubernetesConfig config) { + ImageConfiguration imageConfig = getImageConfiguration(getProject(), config, configurationRegistry); - Optional existingDeployment = resources.groups().getOrDefault(MINIKUBE, new KubernetesListBuilder()) + Optional existingDeployment = resourceRegistry.groups().getOrDefault(MINIKUBE, new KubernetesListBuilder()) .buildItems().stream() .filter(i -> i instanceof Deployment) .map(i -> (Deployment) i) @@ -83,17 +78,17 @@ public void handle(KubernetesConfig config) { .findAny(); if (!existingDeployment.isPresent()) { - resources.add(MINIKUBE, createDeployment(config, imageConfig)); + resourceRegistry.add(MINIKUBE, createDeployment(config, imageConfig)); } addDecorators(MINIKUBE, config); if (config.isHeadless()) { - resources.decorate(MINIKUBE, new ApplyHeadlessDecorator(config.getName())); + resourceRegistry.decorate(MINIKUBE, new ApplyHeadlessDecorator(config.getName())); } if (config.getReplicas() != 1) { - resources.decorate(MINIKUBE, new ApplyReplicasDecorator(config.getName(), config.getReplicas())); + resourceRegistry.decorate(MINIKUBE, new ApplyReplicasDecorator(config.getName(), config.getReplicas())); } String image = Strings.isNotNullOrEmpty(imageConfig.getImage()) @@ -103,10 +98,10 @@ public void handle(KubernetesConfig config) { : imageConfig.getRegistry(), imageConfig.getGroup(), imageConfig.getName(), imageConfig.getVersion()); - resources.decorate(MINIKUBE, new ApplyImageDecorator(config.getName(), image)); + resourceRegistry.decorate(MINIKUBE, new ApplyImageDecorator(config.getName(), image)); } - public boolean canHandle(Class type) { + public boolean accepts(Class type) { return type.equals(KubernetesConfig.class) || type.equals(EditableKubernetesConfig.class); } @@ -116,14 +111,14 @@ protected void addDecorators(String group, KubernetesConfig config) { super.addDecorators(group, config); for (Container container : config.getInitContainers()) { - resources.decorate(group, new AddInitContainerDecorator(config.getName(), container)); + resourceRegistry.decorate(group, new AddInitContainerDecorator(config.getName(), container)); } if (config.getPorts().length > 0) { - resources.decorate(group, new AddServiceResourceDecorator(config)); + resourceRegistry.decorate(group, new AddServiceResourceDecorator(config)); } - resources.decorate(group, new AddIngressDecorator(config, Labels.createLabelsAsMap(config, "Ingress"))); + resourceRegistry.decorate(group, new AddIngressDecorator(config, Labels.createLabelsAsMap(config, "Ingress"))); } /** @@ -210,8 +205,9 @@ public ConfigurationSupplier getFallbackConfig() { } private static ImageConfiguration getImageConfiguration(Project project, KubernetesConfig appConfig, - Configurators configurators) { - return configurators.getImageConfig(BuildServiceFactories.supplierMatches(project)).map(i -> merge(appConfig, i)) + ConfigurationRegistry configurationRegistry) { + return configurationRegistry.getImageConfig(BuildServiceFactories.supplierMatches(project)) + .map(i -> merge(appConfig, i)) .orElse(ImageConfiguration.from(appConfig)); } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGeneratorFactory.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGeneratorFactory.java new file mode 100644 index 00000000000000..e9580361d0d6ca --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGeneratorFactory.java @@ -0,0 +1,14 @@ + +package io.quarkus.kubernetes.deployment; + +import io.dekorate.ConfigurationRegistry; +import io.dekorate.ManifestGeneratorFactory; +import io.dekorate.ResourceRegistry; + +public class MinikubeManifestGeneratorFactory implements ManifestGeneratorFactory { + + @Override + public MinikubeManifestGenerator create(ResourceRegistry resourceRegistry, ConfigurationRegistry configurationRegistry) { + return new MinikubeManifestGenerator(resourceRegistry, configurationRegistry); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java index f4528cf7d56066..3ba61eb779c342 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java @@ -87,8 +87,10 @@ public List createConfigurators(ApplicationInfoBuildItem List ports) { List result = new ArrayList<>(); - result.addAll(KubernetesCommonHelper.createPlatformConfigurators(config)); - result.addAll(KubernetesCommonHelper.createGlobalConfigurators(ports)); + + KubernetesCommonHelper.combinePorts(ports, config).entrySet().forEach(e -> { + result.add(new ConfiguratorBuildItem(new AddPortToOpenshiftConfig(e.getValue()))); + }); result.add(new ConfiguratorBuildItem(new ApplyExpositionConfigurator(config.route))); if (!capabilities.isPresent(Capability.CONTAINER_IMAGE_S2I) diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveBuilderImageResourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveBuilderImageResourceDecorator.java index 7e6832f4d7215e..acdcdbe45dcc3c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveBuilderImageResourceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveBuilderImageResourceDecorator.java @@ -1,9 +1,14 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.KubernetesListBuilder; +import java.util.List; +import java.util.stream.Collectors; + import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; import io.dekorate.s2i.decorator.AddBuilderImageStreamResourceDecorator; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.openshift.api.model.ImageStream; public class RemoveBuilderImageResourceDecorator extends Decorator { @@ -15,7 +20,13 @@ public RemoveBuilderImageResourceDecorator(String name) { @Override public void visit(KubernetesListBuilder builder) { - builder.removeMatchingFromImageStreamItems(b -> b.build().getMetadata().getName().equalsIgnoreCase(name)); + List imageStreams = builder.getItems().stream() + .filter(i -> i instanceof ImageStream) + .map(i -> (HasMetadata) i) + .filter(i -> i.getMetadata().getName().equalsIgnoreCase(name)) + .collect(Collectors.toList()); + + builder.removeAllFromItems(imageStreams); } @Override diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveCommandDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveCommandDecorator.java index faac14d7273182..80070869c125c0 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveCommandDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveCommandDecorator.java @@ -2,8 +2,8 @@ import java.util.List; -import io.dekorate.deps.kubernetes.api.model.ContainerFluent; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; +import io.fabric8.kubernetes.api.model.ContainerFluent; public class RemoveCommandDecorator extends ApplicationContainerDecorator> { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveDeploymentTriggerDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveDeploymentTriggerDecorator.java index 23c057166e384c..80730366ffdb98 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveDeploymentTriggerDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveDeploymentTriggerDecorator.java @@ -2,11 +2,11 @@ import java.util.Collections; -import io.dekorate.deps.kubernetes.api.model.ObjectMeta; -import io.dekorate.deps.openshift.api.model.DeploymentConfigSpecFluent; import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.kubernetes.decorator.NamedResourceDecorator; import io.dekorate.openshift.decorator.ApplyDeploymentTriggerDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.openshift.api.model.DeploymentConfigSpecFluent; public class RemoveDeploymentTriggerDecorator extends NamedResourceDecorator> { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapEnvSourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapEnvSourceDecorator.java index 1ee3bcff66ccdb..91c943a98aefda 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapEnvSourceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapEnvSourceDecorator.java @@ -1,9 +1,9 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ConfigMapEnvSourceFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.ConfigMapEnvSourceFluent; public class RemoveOptionalFromConfigMapEnvSourceDecorator extends ApplicationContainerDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapKeySelectorDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapKeySelectorDecorator.java index 59d19b5c398514..3f862898b6757c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapKeySelectorDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromConfigMapKeySelectorDecorator.java @@ -1,9 +1,9 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.ConfigMapKeySelectorFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.ConfigMapKeySelectorFluent; public class RemoveOptionalFromConfigMapKeySelectorDecorator extends ApplicationContainerDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretEnvSourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretEnvSourceDecorator.java index c4acfae40e3167..9ba31505158fb5 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretEnvSourceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretEnvSourceDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.SecretEnvSourceFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.SecretEnvSourceFluent; public class RemoveOptionalFromSecretEnvSourceDecorator extends ApplicationContainerDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretKeySelectorDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretKeySelectorDecorator.java index ffd4aaec6fec98..309ec47ed33df5 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretKeySelectorDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RemoveOptionalFromSecretKeySelectorDecorator.java @@ -1,10 +1,10 @@ package io.quarkus.kubernetes.deployment; -import io.dekorate.deps.kubernetes.api.model.SecretKeySelectorFluent; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.Decorator; +import io.fabric8.kubernetes.api.model.SecretKeySelectorFluent; public class RemoveOptionalFromSecretKeySelectorDecorator extends ApplicationContainerDecorator { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java index fa594dae2a7321..beae8e595eb5ce 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java @@ -80,8 +80,9 @@ public void createLabels(KubernetesConfig config, BuildProducer createConfigurators(KubernetesConfig config, List ports) { List result = new ArrayList<>(); - result.addAll(KubernetesCommonHelper.createPlatformConfigurators(config)); - result.addAll(KubernetesCommonHelper.createGlobalConfigurators(ports)); + KubernetesCommonHelper.combinePorts(ports, config).entrySet().forEach(e -> { + result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(e.getValue()))); + }); result.add(new ConfiguratorBuildItem(new ApplyExpositionConfigurator((config.ingress)))); return result; diff --git a/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.HandlerFactory b/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.HandlerFactory deleted file mode 100644 index 4fedf98b8bac70..00000000000000 --- a/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.HandlerFactory +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.kubernetes.deployment.MinikubeHandler diff --git a/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.ManifestGeneratorFactory b/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.ManifestGeneratorFactory new file mode 100644 index 00000000000000..6d0f0372a0438a --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/resources/META-INF/services/io.dekorate.ManifestGeneratorFactory @@ -0,0 +1 @@ +io.quarkus.kubernetes.deployment.MinikubeManifestGeneratorFactory diff --git a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java index eb22ef206c4818..633700df674c08 100644 --- a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java +++ b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java @@ -18,6 +18,9 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Default; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; import org.jboss.logging.Logger; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; @@ -35,7 +38,9 @@ import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.CapabilityBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.IndexDependencyBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; @@ -51,6 +56,7 @@ import io.quarkus.liquibase.runtime.LiquibaseFactoryProducer; import io.quarkus.liquibase.runtime.LiquibaseRecorder; import liquibase.change.Change; +import liquibase.change.DatabaseChangeProperty; import liquibase.change.core.CreateProcedureChange; import liquibase.change.core.CreateViewChange; import liquibase.change.core.LoadDataChange; @@ -69,11 +75,18 @@ class LiquibaseProcessor { private static final String LIQUIBASE_BEAN_NAME_PREFIX = "liquibase_"; + private static final DotName DATABASE_CHANGE_PROPERTY = DotName.createSimple(DatabaseChangeProperty.class.getName()); + @BuildStep CapabilityBuildItem capability() { return new CapabilityBuildItem(Capability.LIQUIBASE); } + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.LIQUIBASE); + } + @BuildStep public SystemPropertyBuildItem disableHub() { // Don't block app startup with prompt: @@ -82,12 +95,18 @@ public SystemPropertyBuildItem disableHub() { return new SystemPropertyBuildItem("liquibase.hub.mode", "off"); } + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + IndexDependencyBuildItem indexLiquibase() { + return new IndexDependencyBuildItem("org.liquibase", "liquibase-core"); + } + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) @Record(STATIC_INIT) void nativeImageConfiguration( LiquibaseRecorder recorder, LiquibaseBuildTimeConfig liquibaseBuildConfig, List jdbcDataSourceBuildItems, + CombinedIndexBuildItem combinedIndex, BuildProducer reflective, BuildProducer resource, BuildProducer services, @@ -122,6 +141,19 @@ void nativeImageConfiguration( reflective.produce(new ReflectiveClassBuildItem(false, false, true, liquibase.change.ConstraintsConfig.class.getName())); + // register classes marked with @DatabaseChangeProperty for reflection + Set classesMarkedWithDatabaseChangeProperty = new HashSet<>(); + for (AnnotationInstance databaseChangePropertyInstance : combinedIndex.getIndex() + .getAnnotations(DATABASE_CHANGE_PROPERTY)) { + // the annotation is only supported on methods but let's be safe + AnnotationTarget annotationTarget = databaseChangePropertyInstance.target(); + if (annotationTarget.kind() == AnnotationTarget.Kind.METHOD) { + classesMarkedWithDatabaseChangeProperty.add(annotationTarget.asMethod().declaringClass().name().toString()); + } + } + reflective.produce( + new ReflectiveClassBuildItem(true, true, true, classesMarkedWithDatabaseChangeProperty.toArray(new String[0]))); + Collection dataSourceNames = jdbcDataSourceBuildItems.stream() .map(i -> i.getName()) .collect(Collectors.toSet()); @@ -196,11 +228,6 @@ private void addService(BuildProducer services, } } - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(Feature.LIQUIBASE); - } - @BuildStep @Record(ExecutionTime.RUNTIME_INIT) void createBeans(LiquibaseRecorder recorder, diff --git a/extensions/logging-json/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/logging-json/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 52d5bb37bc5db5..1204e402130871 100644 --- a/extensions/logging-json/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/logging-json/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -15,4 +15,4 @@ metadata: languages: - "java" - "kotlin" - artifact: "io.quarkus:quarkus-descriptor-json" \ No newline at end of file + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" \ No newline at end of file diff --git a/extensions/logging-sentry/deployment/src/main/java/io/quarkus/logging/sentry/deployment/SentryProcessor.java b/extensions/logging-sentry/deployment/src/main/java/io/quarkus/logging/sentry/deployment/SentryProcessor.java index f301eb67b2a861..e0a1aa150df1f1 100644 --- a/extensions/logging-sentry/deployment/src/main/java/io/quarkus/logging/sentry/deployment/SentryProcessor.java +++ b/extensions/logging-sentry/deployment/src/main/java/io/quarkus/logging/sentry/deployment/SentryProcessor.java @@ -6,8 +6,37 @@ import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.LogHandlerBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.logging.sentry.SentryConfig; import io.quarkus.logging.sentry.SentryHandlerValueFactory; +import io.sentry.Breadcrumb; +import io.sentry.SentryBaseEvent; +import io.sentry.SentryEvent; +import io.sentry.SentryTransaction; +import io.sentry.SpanContext; +import io.sentry.SpanId; +import io.sentry.SpanStatus; +import io.sentry.protocol.App; +import io.sentry.protocol.Browser; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.DebugImage; +import io.sentry.protocol.DebugMeta; +import io.sentry.protocol.Device; +import io.sentry.protocol.Gpu; +import io.sentry.protocol.Mechanism; +import io.sentry.protocol.Message; +import io.sentry.protocol.OperatingSystem; +import io.sentry.protocol.Request; +import io.sentry.protocol.SdkInfo; +import io.sentry.protocol.SdkVersion; +import io.sentry.protocol.SentryException; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryPackage; +import io.sentry.protocol.SentryRuntime; +import io.sentry.protocol.SentryStackFrame; +import io.sentry.protocol.SentryStackTrace; +import io.sentry.protocol.SentryThread; +import io.sentry.protocol.User; class SentryProcessor { @@ -30,4 +59,37 @@ ExtensionSslNativeSupportBuildItem activateSslNativeSupport() { return new ExtensionSslNativeSupportBuildItem(FEATURE); } + @BuildStep + ReflectiveClassBuildItem addReflection() { + return new ReflectiveClassBuildItem(true, true, + Breadcrumb.class.getName(), + SentryBaseEvent.class.getName(), + SentryEvent.class.getName(), + "io.sentry.SentryValues", + SpanContext.class.getName(), + SpanStatus.class.getName(), + SpanId.class.getName(), + App.class.getName(), + Browser.class.getName(), + Contexts.class.getName(), + DebugImage.class.getName(), + DebugMeta.class.getName(), + Device.class.getName(), + Gpu.class.getName(), + Mechanism.class.getName(), + Message.class.getName(), + OperatingSystem.class.getName(), + Request.class.getName(), + SdkInfo.class.getName(), + SdkVersion.class.getName(), + SentryException.class.getName(), + SentryId.class.getName(), + SentryPackage.class.getName(), + SentryRuntime.class.getName(), + SentryStackFrame.class.getName(), + SentryStackTrace.class.getName(), + SentryThread.class.getName(), + SentryTransaction.class.getName(), + User.class.getName()); + } } diff --git a/extensions/narayana-stm/deployment/src/main/java/io/quarkus/narayana/stm/deployment/NarayanaSTMProcessor.java b/extensions/narayana-stm/deployment/src/main/java/io/quarkus/narayana/stm/deployment/NarayanaSTMProcessor.java index b23e3d0b602fa6..bb2082d8e3bcf8 100644 --- a/extensions/narayana-stm/deployment/src/main/java/io/quarkus/narayana/stm/deployment/NarayanaSTMProcessor.java +++ b/extensions/narayana-stm/deployment/src/main/java/io/quarkus/narayana/stm/deployment/NarayanaSTMProcessor.java @@ -18,6 +18,7 @@ import com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple; import com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore; +import com.arjuna.ats.internal.arjuna.utils.SocketProcessId; import com.arjuna.ats.txoj.Lock; import io.quarkus.deployment.Feature; @@ -30,6 +31,7 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.narayana.stm.runtime.NarayanaSTMRecorder; class NarayanaSTMProcessor { @@ -60,8 +62,11 @@ public NativeImageSystemPropertyBuildItem substrateSystemPropertyBuildItem() { // so disable it at runtime @BuildStep() @Record(RUNTIME_INIT) - public void configureRuntimeProperties(NarayanaSTMRecorder recorder) { + public void configureRuntimeProperties(NarayanaSTMRecorder recorder, + BuildProducer runtimeInit) { recorder.disableTransactionStatusManager(); + runtimeInit.produce(new RuntimeInitializedClassBuildItem(SocketProcessId.class.getName())); + runtimeInit.produce(new RuntimeInitializedClassBuildItem(Lock.class.getName())); } // register STM dynamic proxies diff --git a/extensions/oidc-client-filter/deployment/src/test/java/io/quarkus/oidc/client/filter/OidcClientFilterDevModeTest.java b/extensions/oidc-client-filter/deployment/src/test/java/io/quarkus/oidc/client/filter/OidcClientFilterDevModeTest.java index 57f661e37def43..061201df5c8343 100644 --- a/extensions/oidc-client-filter/deployment/src/test/java/io/quarkus/oidc/client/filter/OidcClientFilterDevModeTest.java +++ b/extensions/oidc-client-filter/deployment/src/test/java/io/quarkus/oidc/client/filter/OidcClientFilterDevModeTest.java @@ -45,7 +45,16 @@ public void testGetUserName() { .then() .statusCode(401) .body(equalTo("ProtectedResourceService requires a token")); - test.modifyResourceFile("application.properties", s -> s.replace("#", "")); + test.modifyResourceFile("application.properties", s -> s.replace("#quarkus.oidc-client-filter.register-filter", + "quarkus.oidc-client-filter.register-filter")); + + // OidcClient configuration is not complete - Quarkus should start - but 500 returned + RestAssured.when().get("/frontend/user-before-registering-provider") + .then() + .statusCode(500); + test.modifyResourceFile("application.properties", s -> s.replace("#quarkus.oidc-client.auth-server-url", + "quarkus.oidc-client.auth-server-url")); + // token lifespan (3 secs) is less than the auto-refresh interval so the token should be refreshed immediately RestAssured.when().get("/frontend/user-after-registering-provider") .then() diff --git a/extensions/oidc-client-filter/deployment/src/test/resources/application-oidc-client-filter.properties b/extensions/oidc-client-filter/deployment/src/test/resources/application-oidc-client-filter.properties index b76713f2a80318..0bce3b0c4c593c 100644 --- a/extensions/oidc-client-filter/deployment/src/test/resources/application-oidc-client-filter.properties +++ b/extensions/oidc-client-filter/deployment/src/test/resources/application-oidc-client-filter.properties @@ -2,7 +2,7 @@ quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus/ quarkus.oidc.client-id=quarkus-app quarkus.oidc.credentials.secret=secret -quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url} +#quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url} quarkus.oidc-client.client-id=${quarkus.oidc.client-id} quarkus.oidc-client.credentials.client-secret.value=${quarkus.oidc.credentials.secret} quarkus.oidc-client.credentials.client-secret.method=POST diff --git a/extensions/oidc-client-filter/runtime/src/main/java/io/quarkus/oidc/client/filter/OidcClientRequestFilter.java b/extensions/oidc-client-filter/runtime/src/main/java/io/quarkus/oidc/client/filter/OidcClientRequestFilter.java index add3285bb12447..cc46bedd76a6ee 100644 --- a/extensions/oidc-client-filter/runtime/src/main/java/io/quarkus/oidc/client/filter/OidcClientRequestFilter.java +++ b/extensions/oidc-client-filter/runtime/src/main/java/io/quarkus/oidc/client/filter/OidcClientRequestFilter.java @@ -14,6 +14,7 @@ import org.jboss.logging.Logger; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; +import io.quarkus.oidc.client.runtime.DisabledOidcClientException; import io.quarkus.oidc.common.runtime.OidcConstants; @Provider @@ -28,6 +29,8 @@ public void filter(ClientRequestContext requestContext) throws IOException { try { final String accessToken = getAccessToken(); requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_SCHEME_WITH_SPACE + accessToken); + } catch (DisabledOidcClientException ex) { + requestContext.abortWith(Response.status(500).build()); } catch (Exception ex) { LOG.debugf("Access token is not available, aborting the request with HTTP 401 error: %s", ex.getMessage()); requestContext.abortWith(Response.status(401).build()); diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/DisabledOidcClientException.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/DisabledOidcClientException.java new file mode 100644 index 00000000000000..2122b1dba47df7 --- /dev/null +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/DisabledOidcClientException.java @@ -0,0 +1,20 @@ +package io.quarkus.oidc.client.runtime; + +@SuppressWarnings("serial") +public class DisabledOidcClientException extends RuntimeException { + public DisabledOidcClientException() { + + } + + public DisabledOidcClientException(String errorMessage) { + this(errorMessage, null); + } + + public DisabledOidcClientException(Throwable cause) { + this(null, cause); + } + + public DisabledOidcClientException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } +} diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java index 6120408d8cf616..e8b87012149b7b 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java @@ -1,8 +1,10 @@ package io.quarkus.oidc.client.runtime; import java.io.IOException; +import java.net.ConnectException; import java.nio.charset.StandardCharsets; import java.security.Key; +import java.time.Duration; import java.time.Instant; import java.util.Base64; import java.util.function.Supplier; @@ -29,6 +31,7 @@ public class OidcClientImpl implements OidcClient { private static final Logger LOG = Logger.getLogger(OidcClientImpl.class); + private static final Duration REQUEST_RETRY_BACKOFF_DURATION = Duration.ofSeconds(1); private static final String AUTHORIZATION_HEADER = String.valueOf(HttpHeaders.AUTHORIZATION); private final WebClient client; @@ -84,7 +87,12 @@ public Uni get() { body.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE); body.add(OidcConstants.CLIENT_ASSERTION, OidcCommonUtils.signJwtWithKey(oidcConfig, clientJwtKey)); } - return request.sendForm(body).onItem() + // Retry up to three times with a one second delay between the retries if the connection is closed + Uni> response = request.sendForm(body) + .onFailure(ConnectException.class) + .retry() + .atMost(3); + return response.onItem() .transform(resp -> emitGrantTokens(resp, refresh)); } }); @@ -105,9 +113,11 @@ private Tokens emitGrantTokens(HttpResponse resp, boolean refresh) { } return new Tokens(accessToken, accessTokenExpiresAt, oidcConfig.refreshTokenTimeSkew.orElse(null), refreshToken); } else { - LOG.debugf("%s OidcClient has failed to complete the %s grant request: %s", oidcConfig.getId().get(), - (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType), resp.bodyAsString()); - throw new OidcClientException(); + String errorMessage = resp.bodyAsString(); + LOG.debugf("%s OidcClient has failed to complete the %s grant request: status: %d, error message: %s", + oidcConfig.getId().get(), (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType), resp.statusCode(), + errorMessage); + throw new OidcClientException(errorMessage); } } diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java index 7ea0df89b5fcfb..6304a4d56fdfcb 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java @@ -97,7 +97,13 @@ protected static Uni createOidcClientUni(OidcClientConfig oidcConfig oidcConfig.setId(oidcClientId); } - OidcCommonUtils.verifyCommonConfiguration(oidcConfig, false); + try { + OidcCommonUtils.verifyCommonConfiguration(oidcConfig, false); + } catch (Throwable t) { + String message = String.format("'%s' client configuration is not initialized", oidcClientId); + LOG.debug(message); + return Uni.createFrom().item(new DisabledOidcClient(message)); + } String authServerUriString = OidcCommonUtils.getAuthServerUrl(oidcConfig); @@ -200,17 +206,17 @@ private static class DisabledOidcClient implements OidcClient { @Override public Uni getTokens() { - throw new OidcClientException(message); + throw new DisabledOidcClientException(message); } @Override public Uni refreshTokens(String refreshToken) { - throw new OidcClientException(message); + throw new DisabledOidcClientException(message); } @Override public void close() throws IOException { - throw new OidcClientException(message); + throw new DisabledOidcClientException(message); } } } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index a694ad270cca74..b037aeb527f9b0 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -32,19 +32,21 @@ public static void verifyCommonConfiguration(OidcCommonConfig oidcConfig, boolea final String configPrefix = isServerConfig ? "quarkus.oidc." : "quarkus.oidc-client."; if (!oidcConfig.getAuthServerUrl().isPresent() || !oidcConfig.getClientId().isPresent()) { throw new ConfigurationException( - String.format("Both '%sauth-server-url' and '%sclient-id' properties must be configured", configPrefix)); + String.format("Both '%1$sauth-server-url' and '%1$sclient-id' properties must be configured", + configPrefix)); } Credentials creds = oidcConfig.getCredentials(); if (creds.secret.isPresent() && creds.clientSecret.value.isPresent()) { throw new ConfigurationException( - String.format("'%scredentials.secret' and '%scredentials.client-secret' properties are mutually exclusive", + String.format( + "'%1$scredentials.secret' and '%1$scredentials.client-secret' properties are mutually exclusive", configPrefix)); } if ((creds.secret.isPresent() || creds.clientSecret.value.isPresent()) && creds.jwt.secret.isPresent()) { throw new ConfigurationException( String.format( - "Use only '%scredentials.secret' or '%scredentials.client-secret' or '%scredentials.jwt.secret' property", + "Use only '%1$scredentials.secret' or '%1$scredentials.client-secret' or '%1$scredentials.jwt.secret' property", configPrefix)); } } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java new file mode 100644 index 00000000000000..31ce8eb5a88d9e --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java @@ -0,0 +1,74 @@ +package io.quarkus.oidc.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.quarkus.test.QuarkusDevModeTest; +import io.quarkus.test.common.QuarkusTestResource; + +@QuarkusTestResource(KeycloakDevModeRealmResourceManager.class) +public class CodeFlowDevModeDefaultTenantTestCase { + + private static Class[] testClasses = { + ProtectedResource.class, + UnprotectedResource.class + }; + + @RegisterExtension + static final QuarkusDevModeTest test = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(testClasses) + .addAsResource("application-dev-mode-default-tenant.properties", "application.properties")); + + @Test + public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, InterruptedException { + try (final WebClient webClient = createWebClient()) { + + try { + webClient.getPage("http://localhost:8080/protected"); + fail("Exception is expected because auth-server-url is not available and the authentication can not be completed"); + } catch (FailingHttpStatusCodeException ex) { + // Reported by Quarkus + assertEquals(500, ex.getStatusCode()); + } + + // Enable auth-server-url + test.modifyResourceFile("application.properties", + s -> s.replace("#quarkus.oidc.auth-server-url", "quarkus.oidc.auth-server-url")); + + HtmlPage page = webClient.getPage("http://localhost:8080/protected"); + + assertEquals("Sign in to devmode", page.getTitleText()); + + HtmlForm loginForm = page.getForms().get(0); + + loginForm.getInputByName("username").setValueAttribute("alice-dev-mode"); + loginForm.getInputByName("password").setValueAttribute("alice-dev-mode"); + + page = loginForm.getInputByName("login").click(); + + assertEquals("alice-dev-mode", page.getBody().asText()); + + webClient.getCookieManager().clearCookies(); + } + } + + private WebClient createWebClient() { + WebClient webClient = new WebClient(); + webClient.setCssErrorHandler(new SilentCssErrorHandler()); + return webClient; + } +} diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java new file mode 100644 index 00000000000000..1587f4dd27bc04 --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java @@ -0,0 +1,45 @@ +package io.quarkus.oidc.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.quarkus.test.QuarkusUnitTest; + +public class SecurityDisabledTestCase { + + private static Class[] testClasses = { + UnprotectedResource.class + }; + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(testClasses) + .addAsResource("application-security-disabled.properties", "application.properties")); + + @Test + public void testAccessUnprotectedResource() throws IOException, InterruptedException { + try (final WebClient webClient = createWebClient()) { + + HtmlPage page = webClient.getPage("http://localhost:8081/unprotected"); + assertEquals("unprotected", page.getBody().asText()); + webClient.getCookieManager().clearCookies(); + } + } + + private WebClient createWebClient() { + WebClient webClient = new WebClient(); + webClient.setCssErrorHandler(new SilentCssErrorHandler()); + return webClient; + } +} diff --git a/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties b/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties new file mode 100644 index 00000000000000..66215af62a581b --- /dev/null +++ b/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties @@ -0,0 +1,6 @@ +#quarkus.oidc.auth-server-url=${keycloak.url}/realms/devmode +quarkus.oidc.client-id=client-dev-mode +quarkus.oidc.application-type=web-app + +quarkus.log.category."com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleSheet".level=FATAL + diff --git a/extensions/oidc/deployment/src/test/resources/application-security-disabled.properties b/extensions/oidc/deployment/src/test/resources/application-security-disabled.properties new file mode 100644 index 00000000000000..3b03459d8fcfb5 --- /dev/null +++ b/extensions/oidc/deployment/src/test/resources/application-security-disabled.properties @@ -0,0 +1,3 @@ +quarkus.oidc.tenant-enabled=false +quarkus.http.auth.permission.authenticated.paths=/* +quarkus.http.auth.permission.authenticated.policy=permit diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcConfigurationMetadata.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcConfigurationMetadata.java index b458566d91b476..e941f79f1b0b68 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcConfigurationMetadata.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcConfigurationMetadata.java @@ -1,8 +1,10 @@ package io.quarkus.oidc; import java.util.Collections; +import java.util.List; import java.util.Set; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; public class OidcConfigurationMetadata { @@ -13,6 +15,7 @@ public class OidcConfigurationMetadata { private static final String JWKS_ENDPOINT = "jwks_uri"; private static final String USERINFO_ENDPOINT = "userinfo_endpoint"; private static final String END_SESSION_ENDPOINT = "end_session_endpoint"; + private static final String SCOPES_SUPPORTED = "scopes_supported"; private final String tokenUri; private final String introspectionUri; @@ -74,12 +77,27 @@ public String getEndSessionUri() { return endSessionUri; } + public List getSupportedScopes() { + return getStringList(SCOPES_SUPPORTED); + } + public String getIssuer() { return issuer; } public String get(String propertyName) { - return json != null ? null : json.getString(propertyName); + return json == null ? null : json.getString(propertyName); + } + + public List getStringList(String propertyName) { + JsonArray array = json == null ? null : json.getJsonArray(propertyName); + if (array != null) { + @SuppressWarnings("unchecked") + List values = array.getList(); + return Collections.unmodifiableList(values); + } else { + return null; + } } public boolean contains(String propertyName) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java index a0a8a361268fc3..6e15fb5800484a 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java @@ -102,10 +102,17 @@ private TenantConfigContext getStaticTenantContext(RoutingContext context) { TenantConfigContext configContext = tenantId != null ? tenantConfigBean.getStaticTenantsConfig().get(tenantId) : null; if (configContext == null) { if (tenantId != null && !tenantId.isEmpty()) { - LOG.debugf("No configuration with a tenant id '%s' has been found, using the default configuration"); + LOG.debugf( + "Registered TenantResolver has not provided the configuration for tenant '%s', using the default tenant", + tenantId); } configContext = tenantConfigBean.getDefaultTenant(); } + if (!configContext.ready) { + LOG.debugf("Tenant '%s' is not initialized", tenantId); + configContext = null; + } + return configContext; } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java index e8a08f68037c0d..8e3eec0b7e5e05 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java @@ -1,7 +1,9 @@ package io.quarkus.oidc.runtime; +import java.net.ConnectException; import java.nio.charset.StandardCharsets; import java.security.Key; +import java.time.Duration; import org.jboss.logging.Logger; @@ -23,6 +25,8 @@ public class OidcProviderClient { private static final Logger LOG = Logger.getLogger(OidcProviderClient.class); + + private static final Duration REQUEST_RETRY_BACKOFF_DURATION = Duration.ofSeconds(1); private static final String AUTHORIZATION_HEADER = String.valueOf(HttpHeaders.AUTHORIZATION); private final WebClient client; @@ -104,7 +108,12 @@ private UniOnItem> getHttpResponse(String uri, MultiMap req } else { reqBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); } - return request.sendForm(reqBody).onItem(); + // Retry up to three times with a one second delay between the retries if the connection is closed. + Uni> response = request.sendForm(reqBody) + .onFailure(ConnectException.class) + .retry() + .atMost(3); + return response.onItem(); } private AuthorizationCodeTokens getAuthorizationCodeTokens(HttpResponse resp) { @@ -127,11 +136,9 @@ private JsonObject getJsonObject(HttpResponse resp) { if (resp.statusCode() == 200) { return resp.bodyAsJsonObject(); } else { - String error = resp.bodyAsString(); - if (error != null) { - LOG.debugf("Request has failed: %s", error); - } - throw new OIDCException(); + String errorMessage = resp.bodyAsString(); + LOG.debugf("Request has failed: status: %d, error message: %s", resp.statusCode(), errorMessage); + throw new OIDCException(errorMessage); } } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 870965b1d18439..5b9991f127bafa 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -81,7 +81,9 @@ private Uni createDynamicTenantContext(Vertx vertx, OidcTenantConfig oidcConfig, TlsConfig tlsConfig, String tenantId) { if (!dynamicTenantsConfig.containsKey(tenantId)) { - return createTenantContext(vertx, oidcConfig, tlsConfig, tenantId).onItem().transform( + Uni uniContext = createTenantContext(vertx, oidcConfig, tlsConfig, tenantId); + uniContext.onFailure().transform(t -> logTenantConfigContextFailure(t, tenantId)); + return uniContext.onItem().transform( new Function() { @Override public TenantConfigContext apply(TenantConfigContext t) { @@ -97,7 +99,23 @@ public TenantConfigContext apply(TenantConfigContext t) { private TenantConfigContext createStaticTenantContext(Vertx vertx, OidcTenantConfig oidcConfig, TlsConfig tlsConfig, String tenantId) { - return createTenantContext(vertx, oidcConfig, tlsConfig, tenantId).await().indefinitely(); + Uni uniContext = createTenantContext(vertx, oidcConfig, tlsConfig, tenantId); + return uniContext.onFailure() + .recoverWithItem(new Function() { + @Override + public TenantConfigContext apply(Throwable t) { + logTenantConfigContextFailure(t, tenantId); + return new TenantConfigContext(null, oidcConfig, false); + } + }) + .await().indefinitely(); + } + + private static Throwable logTenantConfigContextFailure(Throwable t, String tenantId) { + LOG.debugf( + "'%s' tenant initialization has failed: '%s'. Access to resources protected by this tenant will fail with HTTP 401.", + tenantId, t.getMessage()); + return t; } private Uni createTenantContext(Vertx vertx, OidcTenantConfig oidcConfig, TlsConfig tlsConfig, @@ -107,14 +125,18 @@ private Uni createTenantContext(Vertx vertx, OidcTenantConf } if (!oidcConfig.tenantEnabled) { LOG.debugf("'%s' tenant configuration is disabled", tenantId); - Uni.createFrom().item(new TenantConfigContext(new OidcProvider(null, null, null), oidcConfig)); + return Uni.createFrom().item(new TenantConfigContext(new OidcProvider(null, null, null), oidcConfig)); } if (oidcConfig.getPublicKey().isPresent()) { return Uni.createFrom().item(createTenantContextFromPublicKey(oidcConfig)); } - OidcCommonUtils.verifyCommonConfiguration(oidcConfig, true); + try { + OidcCommonUtils.verifyCommonConfiguration(oidcConfig, true); + } catch (Throwable t) { + return Uni.createFrom().failure(t); + } if (!oidcConfig.discoveryEnabled) { if (oidcConfig.applicationType != ApplicationType.SERVICE) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigContext.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigContext.java index de54cee456776e..3f2185217f2088 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigContext.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigContext.java @@ -14,8 +14,15 @@ class TenantConfigContext { */ final OidcTenantConfig oidcConfig; + final boolean ready; + public TenantConfigContext(OidcProvider client, OidcTenantConfig config) { + this(client, config, true); + } + + public TenantConfigContext(OidcProvider client, OidcTenantConfig config, boolean ready) { this.provider = client; this.oidcConfig = config; + this.ready = ready; } } diff --git a/extensions/opentelemetry-exporter-jaeger/deployment/pom.xml b/extensions/opentelemetry-exporter-jaeger/deployment/pom.xml new file mode 100644 index 00000000000000..911da7e95f2e93 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/deployment/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger-parent + 999-SNAPSHOT + + + quarkus-opentelemetry-exporter-jaeger-deployment + Quarkus - OpenTelemetry Exporter - Jaeger - Deployment + + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger + + + + io.quarkus + quarkus-opentelemetry-deployment + + + io.quarkus + quarkus-grpc-common-deployment + + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + + + org.assertj + assertj-core + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/opentelemetry-exporter-jaeger/deployment/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProcessor.java b/extensions/opentelemetry-exporter-jaeger/deployment/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProcessor.java new file mode 100644 index 00000000000000..390c74fe1fb352 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/deployment/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProcessor.java @@ -0,0 +1,40 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import java.util.function.BooleanSupplier; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +public class JaegerExporterProcessor { + + static class JaegerExporterEnabled implements BooleanSupplier { + JaegerExporterConfig.JaegerExporterBuildConfig jaegerExporterConfig; + + public boolean getAsBoolean() { + return jaegerExporterConfig.enabled; + } + } + + @BuildStep(onlyIf = JaegerExporterEnabled.class) + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.OPENTELEMETRY_JAEGER_EXPORTER); + } + + @BuildStep(onlyIf = JaegerExporterEnabled.class) + AdditionalBeanBuildItem createBatchSpanProcessor() { + return AdditionalBeanBuildItem.builder() + .addBeanClass(JaegerExporterProvider.class) + .setUnremovable().build(); + } + + @BuildStep(onlyIf = JaegerExporterEnabled.class) + @Record(ExecutionTime.RUNTIME_INIT) + void installBatchSpanProcessorForJaeger(JaegerRecorder recorder, + JaegerExporterConfig.JaegerExporterRuntimeConfig runtimeConfig) { + recorder.installBatchSpanProcessorForJaeger(runtimeConfig); + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterBadEndpointTest.java b/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterBadEndpointTest.java new file mode 100644 index 00000000000000..9beb2a31325f4a --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterBadEndpointTest.java @@ -0,0 +1,29 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.OpenTelemetry; +import io.quarkus.test.QuarkusUnitTest; + +public class JaegerExporterBadEndpointTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .overrideConfigKey("quarkus.opentelemetry.tracer.exporter.jaeger.endpoint", "httz://nada:zero") + .setExpectedException(IllegalStateException.class); + + @Inject + OpenTelemetry openTelemetry; + + @Test + void failStart() { + Assertions.fail("Test should not be run as deployment should fail"); + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterDisabledTest.java b/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterDisabledTest.java new file mode 100644 index 00000000000000..ae06ee296200aa --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/deployment/src/test/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterDisabledTest.java @@ -0,0 +1,33 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import javax.enterprise.inject.Instance; +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.OpenTelemetry; +import io.quarkus.test.QuarkusUnitTest; + +public class JaegerExporterDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .overrideConfigKey("quarkus.opentelemetry.tracer.exporter.jaeger.enabled", "false"); + + @Inject + OpenTelemetry openTelemetry; + + @Inject + Instance lateBoundBatchSpanProcessorInstance; + + @Test + void testOpenTelemetryButNoBatchSpanProcessor() { + Assertions.assertNotNull(openTelemetry); + Assertions.assertFalse(lateBoundBatchSpanProcessorInstance.isResolvable()); + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/pom.xml b/extensions/opentelemetry-exporter-jaeger/pom.xml new file mode 100644 index 00000000000000..cf08e24c5b0870 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-opentelemetry-exporter-jaeger-parent + Quarkus - OpenTelemetry Exporter - Jaeger + pom + + deployment + runtime + + diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/pom.xml b/extensions/opentelemetry-exporter-jaeger/runtime/pom.xml new file mode 100644 index 00000000000000..e23503dbf5d970 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + io.quarkus + quarkus-opentelemetry-exporter-jaeger-parent + 999-SNAPSHOT + + + quarkus-opentelemetry-exporter-jaeger + Quarkus - OpenTelemetry Exporter - Jaeger - Runtime + Enable Jaeger Exporter for OpenTelemetry + + + + io.quarkus + quarkus-core + + + io.quarkus + quarkus-arc + + + + io.quarkus + quarkus-opentelemetry + + + io.quarkus + quarkus-grpc-common + + + + io.opentelemetry + opentelemetry-exporter-jaeger + + + org.codehaus.mojo + animal-sniffer-annotations + + + org.checkerframework + checker-qual + + + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterConfig.java b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterConfig.java new file mode 100644 index 00000000000000..fda4345c60f6e5 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterConfig.java @@ -0,0 +1,40 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import java.time.Duration; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +public class JaegerExporterConfig { + @ConfigRoot(name = "opentelemetry.tracer.exporter.jaeger", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) + public static class JaegerExporterBuildConfig { + /** + * Jaeger SpanExporter support. + *

+ * Jaeger SpanExporter support is enabled by default. + */ + @ConfigItem(defaultValue = "true") + public Boolean enabled; + } + + @ConfigRoot(name = "opentelemetry.tracer.exporter.jaeger", phase = ConfigPhase.RUN_TIME) + public static class JaegerExporterRuntimeConfig { + /** + * The Jaeger endpoint to connect to. The endpoint must start with either http:// or https://. + *

+ * Defaults to `http://localhost:14250` if unset. + */ + @ConfigItem(defaultValue = "http://localhost:14250") + public String endpoint; + + /** + * The maximum amount of time to wait for the collector to process exported spans before an exception is thrown. + * A value of `0` will disable the timeout: the exporter will continue waiting until either exported spans are + * processed, + * or the connection fails, or is closed for some other reason. + */ + @ConfigItem(defaultValue = "10S") + public Duration exportTimeout; + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProvider.java b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProvider.java new file mode 100644 index 00000000000000..fc8a129a9b1244 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerExporterProvider.java @@ -0,0 +1,16 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +import io.quarkus.arc.DefaultBean; + +@Singleton +public class JaegerExporterProvider { + @Produces + @Singleton + @DefaultBean + public LateBoundBatchSpanProcessor batchSpanProcessorForJaeger() { + return new LateBoundBatchSpanProcessor(); + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerRecorder.java b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerRecorder.java new file mode 100644 index 00000000000000..97a62b019603a8 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/JaegerRecorder.java @@ -0,0 +1,31 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import javax.enterprise.inject.Any; +import javax.enterprise.inject.spi.CDI; + +import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class JaegerRecorder { + public void installBatchSpanProcessorForJaeger(JaegerExporterConfig.JaegerExporterRuntimeConfig runtimeConfig) { + + // Only create the JaegerGrpcSpanExporter if an endpoint was set in runtime config + if (runtimeConfig.endpoint != null && runtimeConfig.endpoint.trim().length() > 0) { + try { + JaegerGrpcSpanExporter jaegerSpanExporter = JaegerGrpcSpanExporter.builder() + .setEndpoint(runtimeConfig.endpoint) + .setTimeout(runtimeConfig.exportTimeout) + .build(); + + // Create BatchSpanProcessor for Jaeger and install into LateBoundBatchSpanProcessor + LateBoundBatchSpanProcessor delayedProcessor = CDI.current() + .select(LateBoundBatchSpanProcessor.class, Any.Literal.INSTANCE).get(); + delayedProcessor.setBatchSpanProcessorDelegate(BatchSpanProcessor.builder(jaegerSpanExporter).build()); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("Unable to install Jaeger Exporter", iae); + } + } + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/LateBoundBatchSpanProcessor.java b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/LateBoundBatchSpanProcessor.java new file mode 100644 index 00000000000000..27bd40d3653602 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/java/io/quarkus/opentelemetry/exporter/jaeger/LateBoundBatchSpanProcessor.java @@ -0,0 +1,112 @@ +package io.quarkus.opentelemetry.exporter.jaeger; + +import org.jboss.logging.Logger; + +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; + +/** + * Class to facilitate a delay in when the worker thread inside {@link BatchSpanProcessor} + * is started, enabling Quarkus to instantiate a {@link io.opentelemetry.api.trace.TracerProvider} + * during static initialization and set a {@link BatchSpanProcessor} delegate during runtime initialization. + */ +public class LateBoundBatchSpanProcessor implements SpanProcessor { + private static final Logger log = Logger.getLogger(LateBoundBatchSpanProcessor.class); + + private boolean warningLogged = false; + private BatchSpanProcessor delegate; + + /** + * Set the actual {@link BatchSpanProcessor} to use as the delegate. + * + * @param delegate Properly constructed {@link BatchSpanProcessor} for processing spans. + */ + public void setBatchSpanProcessorDelegate(BatchSpanProcessor delegate) { + this.delegate = delegate; + } + + @Override + public void onStart(Context parentContext, ReadWriteSpan span) { + if (delegate == null) { + logDelegateNotFound(); + return; + } + delegate.onStart(parentContext, span); + } + + @Override + public boolean isStartRequired() { + if (delegate == null) { + logDelegateNotFound(); + return false; + } + return delegate.isStartRequired(); + } + + @Override + public void onEnd(ReadableSpan span) { + if (delegate == null) { + logDelegateNotFound(); + return; + } + delegate.onEnd(span); + } + + @Override + public boolean isEndRequired() { + if (delegate == null) { + logDelegateNotFound(); + return false; + } + return delegate.isEndRequired(); + } + + @Override + public CompletableResultCode shutdown() { + if (delegate == null) { + logDelegateNotFound(); + return CompletableResultCode.ofSuccess(); + } + return delegate.shutdown(); + } + + @Override + public CompletableResultCode forceFlush() { + if (delegate == null) { + logDelegateNotFound(); + return CompletableResultCode.ofSuccess(); + } + return delegate.forceFlush(); + } + + @Override + public void close() { + if (delegate != null) { + delegate.close(); + } + resetDelegate(); + } + + /** + * Clear the {@code delegate} and reset {@code warningLogged}. + */ + private void resetDelegate() { + delegate = null; + warningLogged = false; + } + + /** + * If we haven't previously logged an error, + * log an error about a missing {@code delegate} and set {@code warningLogged=true} + */ + private void logDelegateNotFound() { + if (!warningLogged) { + log.warn("No delegate specified, no action taken."); + warningLogged = true; + } + } +} diff --git a/extensions/opentelemetry-exporter-jaeger/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..9ae15434a531f1 --- /dev/null +++ b/extensions/opentelemetry-exporter-jaeger/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,12 @@ +name: "OpenTelemetry exporter: Jaeger" +metadata: + keywords: + - "opentelemetry" + - "tracing" + - "distributed-tracing" + - "opentelemetry-jaeger-exporter" + - "monitoring" + guide: "https://quarkus.io/guides/opentelemetry" + categories: + - "observability" + status: "experimental" diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml new file mode 100644 index 00000000000000..e85330cfa34b1e --- /dev/null +++ b/extensions/opentelemetry/deployment/pom.xml @@ -0,0 +1,98 @@ + + + + quarkus-opentelemetry-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-opentelemetry-deployment + Quarkus - OpenTelemetry - Deployment + + + + io.quarkus + quarkus-opentelemetry + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-smallrye-context-propagation-deployment + + + + io.quarkus + quarkus-vertx-http-deployment + + + + io.quarkus + quarkus-rest-client-deployment + true + + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + + + org.assertj + assertj-core + test + + + + io.quarkus + quarkus-smallrye-opentracing-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProcessor.java new file mode 100644 index 00000000000000..99d2d38d777eaa --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProcessor.java @@ -0,0 +1,59 @@ +package io.quarkus.opentelemetry; + +import java.util.Optional; +import java.util.function.BooleanSupplier; + +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.opentelemetry.tracing.TracerProviderBuildItem; +import io.quarkus.runtime.RuntimeValue; + +public class OpenTelemetryProcessor { + + static class OpenTelemetryEnabled implements BooleanSupplier { + OpenTelemetryConfig otelConfig; + + public boolean getAsBoolean() { + return otelConfig.enabled; + } + } + + @BuildStep(onlyIf = OpenTelemetryEnabled.class) + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.OPENTELEMETRY); + } + + @BuildStep(onlyIf = OpenTelemetryEnabled.class) + AdditionalBeanBuildItem ensureProducerIsRetained() { + return AdditionalBeanBuildItem.builder() + .setUnremovable() + .addBeanClass(OpenTelemetryProducer.class) + .build(); + } + + @BuildStep(onlyIf = OpenTelemetryEnabled.class) + void registerOpenTelemetryContextStorage( + BuildProducer resource, + BuildProducer reflectiveClass) { + resource.produce(new NativeImageResourceBuildItem( + "META-INF/services/io.opentelemetry.context.ContextStorageProvider")); + reflectiveClass + .produce(new ReflectiveClassBuildItem(true, true, QuarkusContextStorage.class)); + } + + @BuildStep(onlyIf = OpenTelemetryEnabled.class) + @Record(ExecutionTime.STATIC_INIT) + void createOpenTelemetry(OpenTelemetryRecorder recorder, Optional tracerProviderBuildItem) { + RuntimeValue tracerProvider = tracerProviderBuildItem.map(TracerProviderBuildItem::getTracerProvider) + .orElse(null); + recorder.createOpenTelemetry(tracerProvider); + } +} diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProcessor.java new file mode 100644 index 00000000000000..003c8f9da179e0 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProcessor.java @@ -0,0 +1,125 @@ +package io.quarkus.opentelemetry.tracing; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BooleanSupplier; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; + +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.arc.processor.DotNames; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; +import io.quarkus.deployment.builditem.CapabilityBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.ShutdownContextBuildItem; +import io.quarkus.opentelemetry.OpenTelemetryConfig; +import io.quarkus.opentelemetry.tracing.vertx.VertxTracerFilter; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.vertx.http.deployment.FilterBuildItem; + +public class TracerProcessor { + private static final DotName SPAN_EXPORTER = DotName.createSimple(SpanExporter.class.getName()); + private static final DotName SPAN_PROCESSOR = DotName.createSimple(SpanProcessor.class.getName()); + + public static class TracerEnabled implements BooleanSupplier { + OpenTelemetryConfig otelConfig; + + public boolean getAsBoolean() { + return otelConfig.tracer.enabled.map(tracerEnabled -> otelConfig.enabled && tracerEnabled) + .orElseGet(() -> otelConfig.enabled); + } + } + + @BuildStep(onlyIf = TracerEnabled.class) + CapabilityBuildItem tracingCapability() { + return new CapabilityBuildItem(Capability.OPENTELEMETRY_TRACER); + } + + @BuildStep(onlyIf = TracerEnabled.class) + UnremovableBeanBuildItem ensureProducersAreRetained( + CombinedIndexBuildItem indexBuildItem, + Capabilities capabilities, + BuildProducer additionalBeans) { + + if (capabilities.isPresent(Capability.OPENTRACING) || + capabilities.isPresent(Capability.SMALLRYE_OPENTRACING)) { + throw new ConfigurationException("The OpenTelemetry extension tracer can not be used in " + + "conjunction with either the SmallRye OpenTracing or Jaeger extensions."); + } + + additionalBeans.produce(AdditionalBeanBuildItem.builder() + .setUnremovable() + .addBeanClass(TracerProducer.class) + .build()); + + IndexView index = indexBuildItem.getIndex(); + + // Find all known SpanExporters and SpanProcessors + Collection knownClasses = new HashSet<>(); + knownClasses.add(SPAN_EXPORTER.toString()); + index.getAllKnownImplementors(SPAN_EXPORTER) + .forEach(classInfo -> knownClasses.add(classInfo.name().toString())); + knownClasses.add(SPAN_PROCESSOR.toString()); + index.getAllKnownImplementors(SPAN_PROCESSOR) + .forEach(classInfo -> knownClasses.add(classInfo.name().toString())); + + Set retainProducers = new HashSet<>(); + + for (AnnotationInstance annotation : index.getAnnotations(DotNames.PRODUCES)) { + AnnotationTarget target = annotation.target(); + switch (target.kind()) { + case METHOD: + MethodInfo method = target.asMethod(); + String returnType = method.returnType().name().toString(); + if (knownClasses.contains(returnType)) { + retainProducers.add(method.declaringClass().name().toString()); + } + break; + case FIELD: + FieldInfo field = target.asField(); + String fieldType = field.type().name().toString(); + if (knownClasses.contains(fieldType)) { + retainProducers.add(field.declaringClass().name().toString()); + } + break; + default: + break; + } + } + + return new UnremovableBeanBuildItem(new UnremovableBeanBuildItem.BeanClassNamesExclusion(retainProducers)); + } + + //TODO This needs modification to use conditional behaviors when it's present + @BuildStep(onlyIf = TracerEnabled.class) + FilterBuildItem addVertxTracerFilter() { + return new FilterBuildItem(new VertxTracerFilter(), Integer.MAX_VALUE); + } + + @BuildStep(onlyIf = TracerEnabled.class) + @Record(ExecutionTime.STATIC_INIT) + TracerProviderBuildItem createTracerProvider(TracerRecorder recorder, + ApplicationInfoBuildItem appInfo, + ShutdownContextBuildItem shutdownContext, + BeanContainerBuildItem beanContainerBuildItem) { + String serviceName = appInfo.getName(); + String serviceVersion = appInfo.getVersion(); + return new TracerProviderBuildItem(recorder.createTracerProvider(serviceName, serviceVersion, shutdownContext)); + } +} diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProviderBuildItem.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProviderBuildItem.java new file mode 100644 index 00000000000000..3af3b2993ee3ba --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/tracing/TracerProviderBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.opentelemetry.tracing; + +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; + +public final class TracerProviderBuildItem extends SimpleBuildItem { + + private final RuntimeValue tracerProvider; + + TracerProviderBuildItem(RuntimeValue tracerProvider) { + this.tracerProvider = tracerProvider; + } + + public RuntimeValue getTracerProvider() { + return tracerProvider; + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/OpenTelemetryDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/OpenTelemetryDisabledTest.java new file mode 100644 index 00000000000000..a65f1630c3dcdc --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/OpenTelemetryDisabledTest.java @@ -0,0 +1,32 @@ +package io.quarkus.opentelemetry; + +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.OpenTelemetry; +import io.quarkus.test.QuarkusUnitTest; + +public class OpenTelemetryDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .overrideConfigKey("quarkus.opentelemetry.enabled", "false") + .assertException(t -> Assertions.assertEquals(DeploymentException.class, t.getClass())); + + @Inject + OpenTelemetry openTelemetry; + + @Test + void testNoOpenTelemetry() { + //Should not be reached: dump what was injected if it somehow passed + Assertions.assertNull(openTelemetry, + "A OpenTelemetry instance should not be found/injected when OpenTelemetry is disabled"); + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerDisabledTest.java new file mode 100644 index 00000000000000..15acc1185cedbc --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerDisabledTest.java @@ -0,0 +1,32 @@ +package io.quarkus.opentelemetry; + +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.trace.Tracer; +import io.quarkus.test.QuarkusUnitTest; + +public class TracerDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .overrideConfigKey("quarkus.opentelemetry.tracer.enabled", "false") + .assertException(t -> Assertions.assertEquals(DeploymentException.class, t.getClass())); + + @Inject + Tracer tracer; + + @Test + void testNoTracer() { + //Should not be reached: dump what was injected if it somehow passed + Assertions.assertNull(tracer, + "A Tracer instance should not be found/injected when OpenTelemetry tracer is disabled"); + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerWithInvalidExtensionTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerWithInvalidExtensionTest.java new file mode 100644 index 00000000000000..93c48e6970abfc --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/TracerWithInvalidExtensionTest.java @@ -0,0 +1,28 @@ +package io.quarkus.opentelemetry; + +import java.util.Collections; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusProdModeTest; + +public class TracerWithInvalidExtensionTest { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .setForcedDependencies( + Collections.singletonList( + new AppArtifact("io.quarkus", "quarkus-smallrye-opentracing", "999-SNAPSHOT"))) + .setExpectedException(ConfigurationException.class); + + @Test + void failStart() { + Assertions.fail("Test should not be run as deployment should fail"); + } +} diff --git a/extensions/opentelemetry/pom.xml b/extensions/opentelemetry/pom.xml new file mode 100644 index 00000000000000..2b07fb20908fb6 --- /dev/null +++ b/extensions/opentelemetry/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-opentelemetry-parent + Quarkus - OpenTelemetry + pom + + deployment + runtime + + diff --git a/extensions/opentelemetry/runtime/pom.xml b/extensions/opentelemetry/runtime/pom.xml new file mode 100644 index 00000000000000..fa9dea0954c2a6 --- /dev/null +++ b/extensions/opentelemetry/runtime/pom.xml @@ -0,0 +1,71 @@ + + + + quarkus-opentelemetry-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-opentelemetry + Quarkus - OpenTelemetry - Runtime + Use OpenTelemetry to trace services + + + + io.quarkus + quarkus-core + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-smallrye-context-propagation + + + + io.quarkus + quarkus-vertx-http + + + + io.quarkus + quarkus-rest-client + true + + + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-semconv + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryConfig.java new file mode 100644 index 00000000000000..586da749644a29 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryConfig.java @@ -0,0 +1,20 @@ +package io.quarkus.opentelemetry; + +import io.quarkus.opentelemetry.tracing.TracerConfig; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(name = "opentelemetry") +public final class OpenTelemetryConfig { + + /** + * OpenTelemetry support. + *

+ * OpenTelemetry support is enabled by default. + */ + @ConfigItem(defaultValue = "true") + public boolean enabled; + + /** Build / static runtime config for tracer */ + public TracerConfig tracer; +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextProvider.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextProvider.java new file mode 100644 index 00000000000000..ec38a91b05e138 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextProvider.java @@ -0,0 +1,40 @@ +package io.quarkus.opentelemetry; + +import java.util.Map; + +import org.eclipse.microprofile.context.spi.ThreadContextProvider; +import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; + +public class OpenTelemetryContextProvider implements ThreadContextProvider { + @Override + public ThreadContextSnapshot currentContext(Map map) { + Context capturedContext = Context.current(); + return () -> { + Context context = Context.current(); + if (capturedContext != context) { + capturedContext.makeCurrent(); + } + return () -> { + if (Context.current() != context) { + context.makeCurrent(); + } + }; + }; + } + + @Override + public ThreadContextSnapshot clearedContext(Map map) { + return () -> { + Scope emptyScope = Context.root().makeCurrent(); + return emptyScope::close; + }; + } + + @Override + public String getThreadContextType() { + return "OpenTelemetry"; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextStorageProvider.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextStorageProvider.java new file mode 100644 index 00000000000000..513d46a11e276e --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryContextStorageProvider.java @@ -0,0 +1,11 @@ +package io.quarkus.opentelemetry; + +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.ContextStorageProvider; + +public class OpenTelemetryContextStorageProvider implements ContextStorageProvider { + @Override + public ContextStorage get() { + return QuarkusContextStorage.INSTANCE; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProducer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProducer.java new file mode 100644 index 00000000000000..110873e5e72f0b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryProducer.java @@ -0,0 +1,18 @@ +package io.quarkus.opentelemetry; + +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.quarkus.arc.DefaultBean; + +@Singleton +public class OpenTelemetryProducer { + @Produces + @Singleton + @DefaultBean + public OpenTelemetry getOpenTelemetry() { + return GlobalOpenTelemetry.get(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryRecorder.java new file mode 100644 index 00000000000000..3fdb8b4c77e5da --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryRecorder.java @@ -0,0 +1,26 @@ +package io.quarkus.opentelemetry; + +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.OpenTelemetrySdkBuilder; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class OpenTelemetryRecorder { + public void createOpenTelemetry(RuntimeValue tracerProvider) { + OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder(); + + // Set tracer provider if present + if (tracerProvider != null) { + builder.setTracerProvider(tracerProvider.getValue()); + } + + // Add propagators. //TODO Need a way to handle this with config + builder.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + + builder.buildAndRegisterGlobal(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/QuarkusContextStorage.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/QuarkusContextStorage.java new file mode 100644 index 00000000000000..938f0ab8ca4e42 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/QuarkusContextStorage.java @@ -0,0 +1,87 @@ +package io.quarkus.opentelemetry; + +import javax.enterprise.context.ContextNotActiveException; +import javax.enterprise.inject.spi.CDI; + +import org.jboss.logging.Logger; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import io.quarkus.vertx.http.runtime.CurrentVertxRequest; +import io.vertx.ext.web.RoutingContext; + +public enum QuarkusContextStorage implements ContextStorage { + INSTANCE; + + private static final Logger log = Logger.getLogger(QuarkusContextStorage.class); + private static final String CONTEXT_KEY = QuarkusContextStorage.class.getName() + ".activeContext"; + + private static final ThreadLocal THREAD_LOCAL_ROUTING_CONTEXT = new ThreadLocal<>(); + + @Override + public Scope attach(Context toAttach) { + if (toAttach == null) { + // Not allowed + return NoopScope.INSTANCE; + } + + Context beforeAttach = current(); + if (toAttach == beforeAttach || beforeAttach == Context.root()) { + return NoopScope.INSTANCE; + } + + RoutingContext routingContext = getRoutingContext(); + if (routingContext != null) { + routingContext.put(CONTEXT_KEY, toAttach); + } else { + log.debug("RoutingContext not available. Unable to set new Context onto it."); + } + + return () -> { + if (routingContext != null) { + routingContext.put(CONTEXT_KEY, beforeAttach); + } + }; + } + + @Override + public Context current() { + RoutingContext routingContext = getRoutingContext(); + if (routingContext != null) { + return routingContext.get(CONTEXT_KEY); + } + return null; + } + + private RoutingContext getRoutingContext() { + try { + RoutingContext currentRoutingContext = CDI.current().select(CurrentVertxRequest.class).get().getCurrent(); + + // We no longer need temporary storage of RoutingContext on the thread as CDI Request Scope is active + THREAD_LOCAL_ROUTING_CONTEXT.remove(); + + return currentRoutingContext; + } catch (ContextNotActiveException cnae) { + return THREAD_LOCAL_ROUTING_CONTEXT.get(); + } + } + + public void setRoutingContext(RoutingContext internalRoutingContext) { + THREAD_LOCAL_ROUTING_CONTEXT.set(internalRoutingContext); + } + + public void clearRoutingContext(RoutingContext routingContext) { + if (routingContext.equals(THREAD_LOCAL_ROUTING_CONTEXT.get())) { + THREAD_LOCAL_ROUTING_CONTEXT.remove(); + } + } + + enum NoopScope implements Scope { + INSTANCE; + + @Override + public void close() { + } + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerConfig.java new file mode 100644 index 00000000000000..09d3aa02d6c1dd --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerConfig.java @@ -0,0 +1,26 @@ +package io.quarkus.opentelemetry.tracing; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class TracerConfig { + /** + * Support for tracing with OpenTelemetry. + *

+ * Support for tracing will be enabled if OpenTelemetry support is enabled + * and either this value is true, or this value is unset. + */ + @ConfigItem + public Optional enabled; + + /** Build / static runtime config for span exporters */ + public SpanExporterConfig exporter; + + /** Build / static runtime config for span exporters */ + @ConfigGroup + public static class SpanExporterConfig { + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerProducer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerProducer.java new file mode 100644 index 00000000000000..4a1f6b0f2e9c88 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerProducer.java @@ -0,0 +1,18 @@ +package io.quarkus.opentelemetry.tracing; + +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Tracer; +import io.quarkus.arc.DefaultBean; + +@Singleton +public class TracerProducer { + @Produces + @Singleton + @DefaultBean + public Tracer getTracer() { + return GlobalOpenTelemetry.getTracer("io.quarkus.opentelemetry"); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerRecorder.java new file mode 100644 index 00000000000000..58101e3cecd06c --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracerRecorder.java @@ -0,0 +1,56 @@ +package io.quarkus.opentelemetry.tracing; + +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.BeanManager; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.quarkus.arc.Arc; +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.ShutdownContext; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class TracerRecorder { + public RuntimeValue createTracerProvider(String serviceName, String serviceVersion, + ShutdownContext shutdownContext) { + BeanManager beanManager = Arc.container().beanManager(); + SdkTracerProviderBuilder builder = SdkTracerProvider.builder(); + + // Define Service Resource + builder.setResource( + Resource.getDefault() + .merge( + Resource.create( + Attributes.of( + ResourceAttributes.SERVICE_NAME, serviceName, + ResourceAttributes.SERVICE_VERSION, serviceVersion)))); + + // Find all SpanExporter instances + Instance allExporters = beanManager.createInstance() + .select(SpanExporter.class, Any.Literal.INSTANCE); + allExporters.forEach(spanExporter -> builder.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))); + + // Find all SpanProcessor instances + Instance allProcessors = beanManager.createInstance() + .select(SpanProcessor.class, Any.Literal.INSTANCE); + allProcessors.forEach(builder::addSpanProcessor); + + SdkTracerProvider tracerProvider = builder.build(); + + // Register shutdown tasks + shutdownContext.addShutdownTask(() -> { + tracerProvider.forceFlush(); + tracerProvider.shutdown(); + }); + + return new RuntimeValue<>(tracerProvider); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracingUtils.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracingUtils.java new file mode 100644 index 00000000000000..973118e0491712 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/TracingUtils.java @@ -0,0 +1,9 @@ +package io.quarkus.opentelemetry.tracing; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; + +public class TracingUtils { + public static final String SPAN_KEY = Span.class.getName() + ".activeSpan"; + public static final String SCOPE_KEY = Scope.class.getName() + ".activeScope"; +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/ClientTracingFilter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/ClientTracingFilter.java new file mode 100644 index 00000000000000..9dddde9cf92344 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/ClientTracingFilter.java @@ -0,0 +1,74 @@ +package io.quarkus.opentelemetry.tracing.client; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.client.jaxrs.internal.ClientRequestContextImpl; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +@Priority(Priorities.HEADER_DECORATOR) +public class ClientTracingFilter implements ClientRequestFilter, ClientResponseFilter { + private static final TextMapPropagator TEXT_MAP_PROPAGATOR = GlobalOpenTelemetry.getPropagators().getTextMapPropagator(); + + private Tracer tracer; + private Span clientSpan; + + public ClientTracingFilter(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + // Create new span + SpanBuilder builder = tracer.spanBuilder(requestContext.getUri().getPath().substring(1)) + .setSpanKind(SpanKind.CLIENT); + + // Add attributes + builder.setAttribute(SemanticAttributes.HTTP_METHOD, + ((ClientRequestContextImpl) requestContext).getInvocation().getMethod()); + // builder.setAttribute(SemanticAttributes.HTTP_URL, ((ClientRequestContextImpl) requestContext).getInvocation().getActualTarget().getUri().) + builder.setAttribute(SemanticAttributes.HTTP_URL, requestContext.getUri().toString()); + + clientSpan = builder.startSpan(); + TEXT_MAP_PROPAGATOR.inject(Context.current().with(clientSpan), requestContext.getHeaders(), SETTER); + } + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + if (clientSpan != null) { + clientSpan.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, responseContext.getStatus()); + + if (!responseContext.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL)) { + // TODO Is this ok as we don't have an exception to call recordException() with? + clientSpan.setStatus(StatusCode.ERROR, responseContext.getStatusInfo().getReasonPhrase()); + } + + clientSpan.end(); + } + } + + private static final TextMapSetter> SETTER = new TextMapSetter>() { + @Override + public void set(MultivaluedMap carrier, String key, String value) { + carrier.add(key, value); + } + }; +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/QuarkusRestClientListener.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/QuarkusRestClientListener.java new file mode 100644 index 00000000000000..bad1501e002d47 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/client/QuarkusRestClientListener.java @@ -0,0 +1,40 @@ +package io.quarkus.opentelemetry.tracing.client; + +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.CDI; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.spi.RestClientListener; +import org.jboss.logging.Logger; + +import io.opentelemetry.api.trace.Tracer; + +public class QuarkusRestClientListener implements RestClientListener { + private static final Logger log = Logger.getLogger(QuarkusRestClientListener.class); + + Tracer tracer; + boolean initialized = false; + boolean tracingEnabled = false; + + @Override + public void onNewClient(Class clientInterface, RestClientBuilder restClientBuilder) { + if (setupTracer()) { + restClientBuilder.register(new ClientTracingFilter(tracer)); + } + } + + private boolean setupTracer() { + if (!initialized) { + Instance tracerInstance = CDI.current().select(Tracer.class); + + if (tracerInstance.isResolvable()) { + tracer = tracerInstance.get(); + tracingEnabled = true; + } else { + log.warn("Unable to resolve Tracer instance, no ClientTracingFilter registered on REST Client."); + } + initialized = true; + } + return tracingEnabled; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/vertx/VertxTracerFilter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/vertx/VertxTracerFilter.java new file mode 100644 index 00000000000000..35212d3c6a89d9 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/tracing/vertx/VertxTracerFilter.java @@ -0,0 +1,136 @@ +package io.quarkus.opentelemetry.tracing.vertx; + +import java.util.Iterator; + +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.CDI; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.quarkus.opentelemetry.QuarkusContextStorage; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.RoutingContext; + +public class VertxTracerFilter implements Handler { + private static final TextMapPropagator TEXT_MAP_PROPAGATOR = GlobalOpenTelemetry.getPropagators().getTextMapPropagator(); + + Tracer tracer; + boolean initialized = false; + + @Override + public void handle(RoutingContext routingContext) { + // Needed because CDI Request Scope not active yet + QuarkusContextStorage.INSTANCE.setRoutingContext(routingContext); + + if (!initialized) { + Instance tracerInstance = CDI.current().select(Tracer.class); + if (tracerInstance.isResolvable()) { + tracer = tracerInstance.get(); + } + initialized = true; + } + + // Don't do anything if there is no Tracer + if (tracer != null) { + // Retrieve any incoming Span + Context parentContext = propagatedContext(routingContext); + + // Create new span + SpanBuilder builder = tracer.spanBuilder(routingContext.request().uri().substring(1)) + .setParent(parentContext) + .setSpanKind(SpanKind.SERVER); + + // Add attributes + builder.setAttribute(SemanticAttributes.HTTP_FLAVOR, convertHttpVersion(routingContext.request().version())); + builder.setAttribute(SemanticAttributes.HTTP_METHOD, routingContext.request().method().name()); + builder.setAttribute(SemanticAttributes.HTTP_TARGET, routingContext.request().path()); + builder.setAttribute(SemanticAttributes.HTTP_SCHEME, routingContext.request().scheme()); + builder.setAttribute(SemanticAttributes.HTTP_HOST, routingContext.request().host()); + builder.setAttribute(SemanticAttributes.HTTP_CLIENT_IP, routingContext.request().remoteAddress().host()); + builder.setAttribute(SemanticAttributes.HTTP_USER_AGENT, routingContext.request().getHeader("User-Agent")); + + String contentLength = routingContext.request().getHeader("Content-Length"); + if (contentLength != null && contentLength.length() > 0 && Long.parseLong(contentLength) > 0) { + builder.setAttribute(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, Long.valueOf(contentLength)); + } else { + builder.setAttribute(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, routingContext.request().bytesRead()); + } + + Span currentSpan = builder.startSpan(); + Scope spanScope = currentSpan.makeCurrent(); + + // Add Handler to finish a Span + HttpServerResponse response = routingContext.response(); + routingContext.addHeadersEndHandler(new Handler() { + @Override + public void handle(Void event) { + currentSpan.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.getStatusCode()); + + if (routingContext.failed()) { + // Add exception to span + currentSpan.setStatus(StatusCode.ERROR); + currentSpan.recordException(routingContext.failure()); + } + + currentSpan.end(); + spanScope.close(); + + QuarkusContextStorage.INSTANCE.clearRoutingContext(routingContext); + } + }); + } + + // Call next Handler + routingContext.next(); + } + + private String convertHttpVersion(HttpVersion version) { + switch (version) { + case HTTP_1_0: + return "1.0"; + case HTTP_1_1: + return "1.1"; + case HTTP_2: + return "2.0"; + default: + return ""; + } + } + + private Context propagatedContext(RoutingContext routingContext) { + // Extract trace from incoming request + return TEXT_MAP_PROPAGATOR.extract(Context.current(), routingContext.request(), GETTER); + } + + private static final TextMapGetter GETTER = new TextMapGetter() { + @Override + public Iterable keys(HttpServerRequest carrier) { + return new Iterable() { + @Override + public Iterator iterator() { + return carrier.headers().names().iterator(); + } + }; + } + + @Override + public String get(HttpServerRequest carrier, String key) { + if (carrier != null) { + return carrier.getHeader(key); + } + return null; + } + }; +} diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/opentelemetry/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..aa4a09111bce74 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,12 @@ +--- +name: "OpenTelemetry" +metadata: + keywords: + - "opentelemetry" + - "tracing" + - "distributed-tracing" + - "monitoring" + guide: "https://quarkus.io/guides/opentelemetry" + categories: + - "observability" + status: "experimental" diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider new file mode 100644 index 00000000000000..d7813331e78032 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider @@ -0,0 +1 @@ +io.quarkus.opentelemetry.OpenTelemetryContextStorageProvider \ No newline at end of file diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider new file mode 100644 index 00000000000000..ff267fbe5d4d6d --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider @@ -0,0 +1 @@ +io.quarkus.opentelemetry.OpenTelemetryContextProvider \ No newline at end of file diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener new file mode 100644 index 00000000000000..445a90a8dce2a3 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener @@ -0,0 +1 @@ +io.quarkus.opentelemetry.tracing.client.QuarkusRestClientListener \ No newline at end of file diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java index d258aa19ace3e1..5af71d2b4b5112 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java @@ -10,6 +10,7 @@ import org.jboss.jandex.IndexView; import org.jboss.jandex.Type; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -21,6 +22,7 @@ import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; import io.quarkus.hibernate.orm.rest.data.panache.runtime.RestDataPanacheExceptionMapper; +import io.quarkus.hibernate.orm.rest.data.panache.runtime.jta.TransactionalUpdateExecutor; import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem; @@ -43,6 +45,11 @@ ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); } + @BuildStep + AdditionalBeanBuildItem registerTransactionalExecutor() { + return AdditionalBeanBuildItem.unremovableOf(TransactionalUpdateExecutor.class); + } + /** * Find Panache entity resources and generate their implementations. */ diff --git a/extensions/panache/hibernate-orm-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/runtime/jta/TransactionalUpdateExecutor.java b/extensions/panache/hibernate-orm-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/runtime/jta/TransactionalUpdateExecutor.java new file mode 100644 index 00000000000000..b96e98ce4fc18e --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/runtime/jta/TransactionalUpdateExecutor.java @@ -0,0 +1,18 @@ +package io.quarkus.hibernate.orm.rest.data.panache.runtime.jta; + +import java.util.function.Supplier; + +import javax.inject.Singleton; +import javax.transaction.Transactional; + +import io.quarkus.rest.data.panache.runtime.UpdateExecutor; + +@Singleton +public class TransactionalUpdateExecutor implements UpdateExecutor { + + @Override + @Transactional + public T execute(Supplier supplier) { + return supplier.get(); + } +} diff --git a/extensions/panache/mongodb-panache-common/deployment/pom.xml b/extensions/panache/mongodb-panache-common/deployment/pom.xml index e00b7bc2c12af1..84697c94276df8 100644 --- a/extensions/panache/mongodb-panache-common/deployment/pom.xml +++ b/extensions/panache/mongodb-panache-common/deployment/pom.xml @@ -37,6 +37,10 @@ io.quarkus quarkus-mongodb-panache-common + + io.quarkus + quarkus-narayana-jta-deployment + io.quarkus diff --git a/extensions/panache/mongodb-panache-common/runtime/pom.xml b/extensions/panache/mongodb-panache-common/runtime/pom.xml index f3e803cac47c5b..03fa31e1295aff 100644 --- a/extensions/panache/mongodb-panache-common/runtime/pom.xml +++ b/extensions/panache/mongodb-panache-common/runtime/pom.xml @@ -43,6 +43,10 @@ quarkus-jackson true + + io.quarkus + quarkus-narayana-jta + org.junit.jupiter junit-jupiter diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java index af67d631c878e5..16de0fdd600d18 100644 --- a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java @@ -154,7 +154,7 @@ public CommonReactivePanacheQueryImpl withReadPreference(R @SuppressWarnings("unchecked") public Uni count() { if (count == null) { - count = collection.countDocuments(mongoQuery); + count = mongoQuery == null ? collection.countDocuments() : collection.countDocuments(mongoQuery); } return count; } diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/CommonPanacheQueryImpl.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/CommonPanacheQueryImpl.java index 2cdaa88be6e2fd..16039f58064a6b 100644 --- a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/CommonPanacheQueryImpl.java @@ -6,10 +6,12 @@ import java.util.Set; import java.util.stream.Stream; +import org.bson.BsonDocument; import org.bson.Document; import org.bson.conversions.Bson; import com.mongodb.ReadPreference; +import com.mongodb.client.ClientSession; import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; @@ -21,6 +23,7 @@ public class CommonPanacheQueryImpl { private MongoCollection collection; + private ClientSession clientSession; private Bson mongoQuery; private Bson sort; private Bson projections; @@ -32,14 +35,17 @@ public class CommonPanacheQueryImpl { private Collation collation; - public CommonPanacheQueryImpl(MongoCollection collection, Bson mongoQuery, Bson sort) { + public CommonPanacheQueryImpl(MongoCollection collection, ClientSession session, Bson mongoQuery, + Bson sort) { this.collection = collection; + this.clientSession = session; this.mongoQuery = mongoQuery; this.sort = sort; } private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, Bson projections, Class documentClass) { this.collection = previousQuery.collection.withDocumentClass(documentClass); + this.clientSession = previousQuery.clientSession; this.mongoQuery = previousQuery.mongoQuery; this.sort = previousQuery.sort; this.projections = projections; @@ -150,7 +156,8 @@ public CommonPanacheQueryImpl withReadPreference(ReadPrefe @SuppressWarnings("unchecked") public long count() { if (count == null) { - count = collection.countDocuments(mongoQuery); + Bson query = getQuery(); + count = clientSession == null ? collection.countDocuments(query) : collection.countDocuments(clientSession, query); } return count; } @@ -162,7 +169,8 @@ public List list() { @SuppressWarnings("unchecked") private List list(Integer limit) { List list = new ArrayList<>(); - FindIterable find = mongoQuery == null ? collection.find() : collection.find(mongoQuery); + Bson query = getQuery(); + FindIterable find = clientSession == null ? collection.find(query) : collection.find(clientSession, query); if (this.projections != null) { find.projection(projections); } @@ -170,15 +178,12 @@ private List list(Integer limit) { find.collation(collation); } manageOffsets(find, limit); - MongoCursor cursor = find.sort(sort).iterator(); - try { + try (MongoCursor cursor = find.sort(sort).iterator()) { while (cursor.hasNext()) { T entity = cursor.next(); list.add(entity); } - } finally { - cursor.close(); } return list; } @@ -232,4 +237,8 @@ private void manageOffsets(FindIterable find, Integer limit) { find.limit(limit); } } + + private Bson getQuery() { + return mongoQuery == null ? new BsonDocument() : mongoQuery; + } } diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java index 9ed4225e275178..0d71402a45334c 100644 --- a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java @@ -14,6 +14,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.transaction.Status; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; + import org.bson.BsonDocument; import org.bson.BsonDocumentWriter; import org.bson.BsonValue; @@ -22,6 +28,7 @@ import org.bson.codecs.EncoderContext; import org.jboss.logging.Logger; +import com.mongodb.client.ClientSession; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -31,15 +38,20 @@ import com.mongodb.client.model.WriteModel; import com.mongodb.client.result.DeleteResult; +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; import io.quarkus.mongodb.panache.MongoEntity; import io.quarkus.mongodb.panache.binder.NativeQueryBinder; import io.quarkus.mongodb.panache.binder.PanacheQlQueryBinder; +import io.quarkus.mongodb.panache.transaction.MongoTransactionException; import io.quarkus.panache.common.Parameters; import io.quarkus.panache.common.Sort; @SuppressWarnings({ "rawtypes", "unchecked" }) public abstract class MongoOperations { - public final String ID = "_id"; + public static final String ID = "_id"; + public static final Object SESSION_KEY = new Object(); + private static final Logger LOGGER = Logger.getLogger(MongoOperations.class); // update operators: https://docs.mongodb.com/manual/reference/operator/update/ @@ -51,7 +63,8 @@ public abstract class MongoOperations { private final Map defaultDatabaseName = new ConcurrentHashMap<>(); - protected abstract QueryType createQuery(MongoCollection collection, Document query, Document sortDoc); + protected abstract QueryType createQuery(MongoCollection collection, ClientSession session, Document query, + Document sortDoc); protected abstract UpdateType createUpdate(MongoCollection collection, Class entityClass, Document docUpdate); @@ -71,34 +84,24 @@ public void persist(Iterable entities) { objects.add(entity); } - if (objects.size() > 0) { - // get the first entity to be able to retrieve the collection with it - Object firstEntity = objects.get(0); - MongoCollection collection = mongoCollection(firstEntity); - persist(collection, objects); - } + persist(objects); } public void persist(Object firstEntity, Object... entities) { - MongoCollection collection = mongoCollection(firstEntity); if (entities == null || entities.length == 0) { + MongoCollection collection = mongoCollection(firstEntity); persist(collection, firstEntity); } else { List entityList = new ArrayList<>(); entityList.add(firstEntity); entityList.addAll(Arrays.asList(entities)); - persist(collection, entityList); + persist(entityList); } } public void persist(Stream entities) { List objects = entities.collect(Collectors.toList()); - if (objects.size() > 0) { - // get the first entity to be able to retrieve the collection with it - Object firstEntity = objects.get(0); - MongoCollection collection = mongoCollection(firstEntity); - update(collection, objects); - } + persist(objects); } public void update(Object entity) { @@ -155,12 +158,7 @@ public void persistOrUpdate(Iterable entities) { objects.add(entity); } - if (objects.size() > 0) { - // get the first entity to be able to retrieve the collection with it - Object firstEntity = objects.get(0); - MongoCollection collection = mongoCollection(firstEntity); - persistOrUpdate(collection, objects); - } + persistOrUpdate(objects); } public void persistOrUpdate(Object firstEntity, Object... entities) { @@ -171,18 +169,13 @@ public void persistOrUpdate(Object firstEntity, Object... entities) { List entityList = new ArrayList<>(); entityList.add(firstEntity); entityList.addAll(Arrays.asList(entities)); - persistOrUpdate(collection, entityList); + persistOrUpdate(entityList); } } public void persistOrUpdate(Stream entities) { List objects = entities.collect(Collectors.toList()); - if (objects.size() > 0) { - // get the first entity to be able to retrieve the collection with it - Object firstEntity = objects.get(0); - MongoCollection collection = mongoCollection(firstEntity); - persistOrUpdate(collection, objects); - } + persistOrUpdate(objects); } public void delete(Object entity) { @@ -190,7 +183,12 @@ public void delete(Object entity) { BsonDocument document = getBsonDocument(collection, entity); BsonValue id = document.get(ID); BsonDocument query = new BsonDocument().append(ID, id); - collection.deleteOne(query); + ClientSession session = getSession(entity); + if (session == null) { + collection.deleteOne(query); + } else { + collection.deleteOne(session, query); + } } public MongoCollection mongoCollection(Class entityClass) { @@ -211,11 +209,26 @@ public MongoDatabase mongoDatabase(Class entityClass) { // Private stuff private void persist(MongoCollection collection, Object entity) { - collection.insertOne(entity); + ClientSession session = getSession(entity); + if (session == null) { + collection.insertOne(entity); + } else { + collection.insertOne(session, entity); + } } - private void persist(MongoCollection collection, List entities) { - collection.insertMany(entities); + private void persist(List entities) { + if (entities.size() > 0) { + // get the first entity to be able to retrieve the collection with it + Object firstEntity = entities.get(0); + MongoCollection collection = mongoCollection(firstEntity); + ClientSession session = getSession(firstEntity); + if (session == null) { + collection.insertMany(entities); + } else { + collection.insertMany(session, entities); + } + } } private void update(MongoCollection collection, Object entity) { @@ -225,7 +238,12 @@ private void update(MongoCollection collection, Object entity) { //then we get its id field and create a new Document with only this one that will be our replace query BsonValue id = document.get(ID); BsonDocument query = new BsonDocument().append(ID, id); - collection.replaceOne(query, entity); + ClientSession session = getSession(entity); + if (session == null) { + collection.replaceOne(query, entity); + } else { + collection.replaceOne(session, query, entity); + } } private void update(MongoCollection collection, List entities) { @@ -238,19 +256,37 @@ private void persistOrUpdate(MongoCollection collection, Object entity) { //we transform the entity as a document first BsonDocument document = getBsonDocument(collection, entity); + ClientSession session = getSession(entity); //then we get its id field and create a new Document with only this one that will be our replace query BsonValue id = document.get(ID); if (id == null) { //insert with autogenerated ID - collection.insertOne(entity); + if (session == null) { + collection.insertOne(entity); + } else { + collection.insertOne(session, entity); + } } else { //insert with user provided ID or update BsonDocument query = new BsonDocument().append(ID, id); - collection.replaceOne(query, entity, new ReplaceOptions().upsert(true)); + if (session == null) { + collection.replaceOne(query, entity, new ReplaceOptions().upsert(true)); + } else { + collection.replaceOne(session, query, entity, new ReplaceOptions().upsert(true)); + } } } - private void persistOrUpdate(MongoCollection collection, List entities) { + private void persistOrUpdate(List entities) { + if (entities.isEmpty()) { + return; + } + + // get the first entity to be able to retrieve the collection with it + Object firstEntity = entities.get(0); + MongoCollection collection = mongoCollection(firstEntity); + ClientSession session = getSession(firstEntity); + //this will be an ordered bulk: it's less performant than a unordered one but will fail at the first failed write List bulk = new ArrayList<>(); for (Object entity : entities) { @@ -270,7 +306,11 @@ private void persistOrUpdate(MongoCollection collection, List entities) } } - collection.bulkWrite(bulk); + if (session == null) { + collection.bulkWrite(bulk); + } else { + collection.bulkWrite(session, bulk); + } } private BsonDocument getBsonDocument(MongoCollection collection, Object entity) { @@ -280,6 +320,61 @@ private BsonDocument getBsonDocument(MongoCollection collection, Object entity) return document; } + ClientSession getSession(Object entity) { + return getSession(entity.getClass()); + } + + ClientSession getSession(Class entityClass) { + MongoEntity mongoEntity = entityClass.getAnnotation(MongoEntity.class); + InstanceHandle instance = Arc.container() + .instance(TransactionSynchronizationRegistry.class); + if (instance.isAvailable()) { + TransactionSynchronizationRegistry registry = instance.get(); + if (registry.getTransactionStatus() == Status.STATUS_ACTIVE) { + ClientSession clientSession = (ClientSession) registry.getResource(SESSION_KEY); + if (clientSession == null) { + return registerClientSession(mongoEntity, registry); + } + } + } + return null; + } + + private ClientSession registerClientSession(MongoEntity mongoEntity, TransactionSynchronizationRegistry registry) { + TransactionManager transactionManager = Arc.container().instance(TransactionManager.class).get(); + MongoClient client = clientFromArc(mongoEntity, MongoClient.class, false); + ClientSession clientSession = client.startSession(); + clientSession.startTransaction();//TODO add txoptions from annotation + registry.putResource(SESSION_KEY, clientSession); + registry.registerInterposedSynchronization(new Synchronization() { + @Override + public void beforeCompletion() { + } + + @Override + public void afterCompletion(int i) { + try { + if (transactionManager.getStatus() == Status.STATUS_ROLLEDBACK) { + try { + clientSession.abortTransaction(); + } finally { + clientSession.close(); + } + } else { + try { + clientSession.commitTransaction(); + } finally { + clientSession.close(); + } + } + } catch (SystemException e) { + throw new MongoTransactionException(e); + } + } + }); + return clientSession; + } + private MongoCollection mongoCollection(Object entity) { Class entityClass = entity.getClass(); return mongoCollection(entityClass); @@ -307,7 +402,9 @@ public String apply(String beanName) { public Object findById(Class entityClass, Object id) { MongoCollection collection = mongoCollection(entityClass); - return collection.find(new Document(ID, id)).first(); + ClientSession session = getSession(entityClass); + return session == null ? collection.find(new Document(ID, id)).first() + : collection.find(session, new Document(ID, id)).first(); } public Optional findByIdOptional(Class entityClass, Object id) { @@ -324,7 +421,8 @@ public QueryType find(Class entityClass, String query, Sort sort, Object... p Document docQuery = Document.parse(bindQuery); Document docSort = sortToDocument(sort); MongoCollection collection = mongoCollection(entityClass); - return createQuery(collection, docQuery, docSort); + ClientSession session = getSession(entityClass); + return createQuery(collection, session, docQuery, docSort); } /** @@ -420,7 +518,8 @@ public QueryType find(Class entityClass, String query, Sort sort, Map entityClass, String query, Parameters params) { @@ -435,12 +534,14 @@ public QueryType find(Class entityClass, String query, Sort sort, Parameters public QueryType find(Class entityClass, Document query, Sort sort) { MongoCollection collection = mongoCollection(entityClass); Document sortDoc = sortToDocument(sort); - return createQuery(collection, query, sortDoc); + ClientSession session = getSession(entityClass); + return createQuery(collection, session, query, sortDoc); } public QueryType find(Class entityClass, Document query, Document sort) { MongoCollection collection = mongoCollection(entityClass); - return createQuery(collection, query, sort); + ClientSession session = getSession(entityClass); + return createQuery(collection, session, query, sort); } public QueryType find(Class entityClass, Document query) { @@ -518,14 +619,16 @@ public Stream stream(Class entityClass, Document query, Document sort) { @SuppressWarnings("rawtypes") public QueryType findAll(Class entityClass) { MongoCollection collection = mongoCollection(entityClass); - return createQuery(collection, null, null); + ClientSession session = getSession(entityClass); + return createQuery(collection, session, null, null); } @SuppressWarnings("rawtypes") public QueryType findAll(Class entityClass, Sort sort) { MongoCollection collection = mongoCollection(entityClass); Document sortDoc = sortToDocument(sort); - return createQuery(collection, null, sortDoc); + ClientSession session = getSession(entityClass); + return createQuery(collection, session, null, sortDoc); } private Document sortToDocument(Sort sort) { @@ -558,21 +661,26 @@ public Stream streamAll(Class entityClass, Sort sort) { public long count(Class entityClass) { MongoCollection collection = mongoCollection(entityClass); - return collection.countDocuments(); + ClientSession session = getSession(entityClass); + return session == null ? collection.countDocuments() : collection.countDocuments(session); } public long count(Class entityClass, String query, Object... params) { String bindQuery = bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); MongoCollection collection = mongoCollection(entityClass); - return collection.countDocuments(docQuery); + + ClientSession session = getSession(entityClass); + return session == null ? collection.countDocuments(docQuery) : collection.countDocuments(session, docQuery); } public long count(Class entityClass, String query, Map params) { String bindQuery = bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); MongoCollection collection = mongoCollection(entityClass); - return collection.countDocuments(docQuery); + + ClientSession session = getSession(entityClass); + return session == null ? collection.countDocuments(docQuery) : collection.countDocuments(session, docQuery); } public long count(Class entityClass, String query, Parameters params) { @@ -582,18 +690,22 @@ public long count(Class entityClass, String query, Parameters params) { //specific Mongo query public long count(Class entityClass, Document query) { MongoCollection collection = mongoCollection(entityClass); - return collection.countDocuments(query); + ClientSession session = getSession(entityClass); + return session == null ? collection.countDocuments(query) : collection.countDocuments(session, query); } public long deleteAll(Class entityClass) { MongoCollection collection = mongoCollection(entityClass); - return collection.deleteMany(new Document()).getDeletedCount(); + ClientSession session = getSession(entityClass); + return session == null ? collection.deleteMany(new Document()).getDeletedCount() + : collection.deleteMany(session, new Document()).getDeletedCount(); } public boolean deleteById(Class entityClass, Object id) { MongoCollection collection = mongoCollection(entityClass); Document query = new Document().append(ID, id); - DeleteResult results = collection.deleteOne(query); + ClientSession session = getSession(entityClass); + DeleteResult results = session == null ? collection.deleteOne(query) : collection.deleteOne(session, query); return results.getDeletedCount() == 1; } @@ -601,14 +713,18 @@ public long delete(Class entityClass, String query, Object... params) { String bindQuery = bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); MongoCollection collection = mongoCollection(entityClass); - return collection.deleteMany(docQuery).getDeletedCount(); + ClientSession session = getSession(entityClass); + return session == null ? collection.deleteMany(docQuery).getDeletedCount() + : collection.deleteMany(session, docQuery).getDeletedCount(); } public long delete(Class entityClass, String query, Map params) { String bindQuery = bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); MongoCollection collection = mongoCollection(entityClass); - return collection.deleteMany(docQuery).getDeletedCount(); + ClientSession session = getSession(entityClass); + return session == null ? collection.deleteMany(docQuery).getDeletedCount() + : collection.deleteMany(session, docQuery).getDeletedCount(); } public long delete(Class entityClass, String query, Parameters params) { @@ -618,7 +734,9 @@ public long delete(Class entityClass, String query, Parameters params) { //specific Mongo query public long delete(Class entityClass, Document query) { MongoCollection collection = mongoCollection(entityClass); - return collection.deleteMany(query).getDeletedCount(); + ClientSession session = getSession(entityClass); + return session == null ? collection.deleteMany(query).getDeletedCount() + : collection.deleteMany(session, query).getDeletedCount(); } public UpdateType update(Class entityClass, String update, Map params) { diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheUpdateImpl.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheUpdateImpl.java index 1cf9e9d7bfac6d..bc6265079e2244 100644 --- a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheUpdateImpl.java +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheUpdateImpl.java @@ -5,6 +5,7 @@ import org.bson.BsonDocument; import org.bson.conversions.Bson; +import com.mongodb.client.ClientSession; import com.mongodb.client.MongoCollection; import io.quarkus.mongodb.panache.PanacheUpdate; @@ -15,26 +16,28 @@ public class PanacheUpdateImpl implements PanacheUpdate { private Class entityClass; private Bson update; private MongoCollection collection; + private ClientSession session; public PanacheUpdateImpl(MongoOperations operations, Class entityClass, Bson update, MongoCollection collection) { this.operations = operations; this.entityClass = entityClass; this.update = update; this.collection = collection; + this.session = operations.getSession(entityClass); } @Override public long where(String query, Object... params) { String bindQuery = operations.bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); - return collection.updateMany(docQuery, update).getModifiedCount(); + return executeUpdate(docQuery); } @Override public long where(String query, Map params) { String bindQuery = operations.bindFilter(entityClass, query, params); BsonDocument docQuery = BsonDocument.parse(bindQuery); - return collection.updateMany(docQuery, update).getModifiedCount(); + return executeUpdate(docQuery); } @Override @@ -44,6 +47,12 @@ public long where(String query, Parameters params) { @Override public long all() { - return collection.updateMany(new BsonDocument(), update).getModifiedCount(); + BsonDocument all = new BsonDocument(); + return executeUpdate(all); + } + + private long executeUpdate(BsonDocument query) { + return session == null ? collection.updateMany(query, update).getModifiedCount() + : collection.updateMany(session, query, update).getModifiedCount(); } } diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/transaction/MongoTransactionException.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/transaction/MongoTransactionException.java new file mode 100644 index 00000000000000..5fdb6e1729bff8 --- /dev/null +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/transaction/MongoTransactionException.java @@ -0,0 +1,7 @@ +package io.quarkus.mongodb.panache.transaction; + +public class MongoTransactionException extends RuntimeException { + public MongoTransactionException(Exception cause) { + super(cause); + } +} diff --git a/extensions/panache/mongodb-panache-kotlin/runtime/src/main/java/io/quarkus/mongodb/panache/kotlin/runtime/PanacheQueryImpl.java b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/java/io/quarkus/mongodb/panache/kotlin/runtime/PanacheQueryImpl.java index 29ef0719bece66..3b5d7d779a7428 100644 --- a/extensions/panache/mongodb-panache-kotlin/runtime/src/main/java/io/quarkus/mongodb/panache/kotlin/runtime/PanacheQueryImpl.java +++ b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/java/io/quarkus/mongodb/panache/kotlin/runtime/PanacheQueryImpl.java @@ -6,6 +6,7 @@ import org.bson.conversions.Bson; import com.mongodb.ReadPreference; +import com.mongodb.client.ClientSession; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Collation; @@ -16,8 +17,8 @@ public class PanacheQueryImpl implements PanacheQuery { private final CommonPanacheQueryImpl delegate; - PanacheQueryImpl(MongoCollection collection, Bson mongoQuery, Bson sort) { - this.delegate = new CommonPanacheQueryImpl<>(collection, mongoQuery, sort); + PanacheQueryImpl(MongoCollection collection, ClientSession session, Bson mongoQuery, Bson sort) { + this.delegate = new CommonPanacheQueryImpl<>(collection, session, mongoQuery, sort); } private PanacheQueryImpl(CommonPanacheQueryImpl delegate) { diff --git a/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/runtime/KotlinMongoOperations.kt b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/runtime/KotlinMongoOperations.kt index 98e8eaec091b45..9332e6a2420d31 100644 --- a/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/runtime/KotlinMongoOperations.kt +++ b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/runtime/KotlinMongoOperations.kt @@ -1,5 +1,6 @@ package io.quarkus.mongodb.panache.kotlin.runtime +import com.mongodb.client.ClientSession import com.mongodb.client.MongoCollection import io.quarkus.mongodb.panache.PanacheUpdate import io.quarkus.mongodb.panache.kotlin.PanacheQuery @@ -30,8 +31,8 @@ class KotlinMongoOperations : MongoOperations, PanacheUpdate>() * * @return the new query implementation */ - override fun createQuery(collection: MongoCollection<*>, query: Document?, sortDoc: Document?) = - PanacheQueryImpl(collection, query, sortDoc) + override fun createQuery(collection: MongoCollection<*>, session: ClientSession?, query: Document?, sortDoc: Document?) = + PanacheQueryImpl(collection, session, query, sortDoc) /** * Creates the update implementation diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/JavaMongoOperations.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/JavaMongoOperations.java index 5b40d09cabe182..82ca2bfa3bafa9 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/JavaMongoOperations.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/JavaMongoOperations.java @@ -5,6 +5,7 @@ import org.bson.Document; +import com.mongodb.client.ClientSession; import com.mongodb.client.MongoCollection; import io.quarkus.mongodb.panache.PanacheQuery; @@ -17,8 +18,8 @@ public class JavaMongoOperations extends MongoOperations, Panach public static final JavaMongoOperations INSTANCE = new JavaMongoOperations(); @Override - protected PanacheQuery createQuery(MongoCollection collection, Document query, Document sortDoc) { - return new PanacheQueryImpl(collection, query, sortDoc); + protected PanacheQuery createQuery(MongoCollection collection, ClientSession session, Document query, Document sortDoc) { + return new PanacheQueryImpl(collection, session, query, sortDoc); } @Override diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheQueryImpl.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheQueryImpl.java index 89d2f81669da39..2b87df395610b1 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheQueryImpl.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/PanacheQueryImpl.java @@ -7,6 +7,7 @@ import org.bson.conversions.Bson; import com.mongodb.ReadPreference; +import com.mongodb.client.ClientSession; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Collation; @@ -16,8 +17,8 @@ public class PanacheQueryImpl implements PanacheQuery { private final CommonPanacheQueryImpl delegate; - PanacheQueryImpl(MongoCollection collection, Bson mongoQuery, Bson sort) { - this.delegate = new CommonPanacheQueryImpl<>(collection, mongoQuery, sort); + PanacheQueryImpl(MongoCollection collection, ClientSession session, Bson mongoQuery, Bson sort) { + this.delegate = new CommonPanacheQueryImpl<>(collection, session, mongoQuery, sort); } private PanacheQueryImpl(CommonPanacheQueryImpl delegate) { diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java index 70a48da899dfae..684a70d9ca9a2f 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java @@ -12,6 +12,7 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -24,6 +25,7 @@ import io.quarkus.gizmo.Gizmo; import io.quarkus.mongodb.rest.data.panache.PanacheMongoEntityResource; import io.quarkus.mongodb.rest.data.panache.PanacheMongoRepositoryResource; +import io.quarkus.mongodb.rest.data.panache.runtime.NoopUpdateExecutor; import io.quarkus.mongodb.rest.data.panache.runtime.RestDataPanacheExceptionMapper; import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; @@ -47,6 +49,11 @@ ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); } + @BuildStep + AdditionalBeanBuildItem registerTransactionalExecutor() { + return AdditionalBeanBuildItem.unremovableOf(NoopUpdateExecutor.class); + } + @BuildStep void findEntityResources(CombinedIndexBuildItem index, BuildProducer implementationsProducer, diff --git a/extensions/panache/mongodb-rest-data-panache/runtime/src/main/java/io/quarkus/mongodb/rest/data/panache/runtime/NoopUpdateExecutor.java b/extensions/panache/mongodb-rest-data-panache/runtime/src/main/java/io/quarkus/mongodb/rest/data/panache/runtime/NoopUpdateExecutor.java new file mode 100644 index 00000000000000..d3023410eff75d --- /dev/null +++ b/extensions/panache/mongodb-rest-data-panache/runtime/src/main/java/io/quarkus/mongodb/rest/data/panache/runtime/NoopUpdateExecutor.java @@ -0,0 +1,16 @@ +package io.quarkus.mongodb.rest.data.panache.runtime; + +import java.util.function.Supplier; + +import javax.inject.Singleton; + +import io.quarkus.rest.data.panache.runtime.UpdateExecutor; + +@Singleton +public class NoopUpdateExecutor implements UpdateExecutor { + + @Override + public T execute(Supplier supplier) { + return supplier.get(); + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/deployment/pom.xml b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/pom.xml new file mode 100644 index 00000000000000..0ded82a136e8df --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + io.quarkus + quarkus-resteasy-reactive-problem-parent + 999-SNAPSHOT + + quarkus-resteasy-reactive-problem-deployment + Quarkus Resteasy Reactive Problem - Deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-resteasy-reactive-problem + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java new file mode 100644 index 00000000000000..2bb609980e13e7 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.problem.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class QuarkusResteasyReactiveProblemProcessor { + + private static final String FEATURE = "quarkus-resteasy-reactive-problem"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java new file mode 100644 index 00000000000000..c754355c392546 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.problem.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class QuarkusResteasyReactiveProblemDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java new file mode 100644 index 00000000000000..4056ed49eb8d70 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.problem.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class QuarkusResteasyReactiveProblemTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/pom.xml b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/pom.xml new file mode 100644 index 00000000000000..013cbbf231fd34 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + io.quarkus + quarkus-resteasy-reactive-problem-parent + 999-SNAPSHOT + + quarkus-resteasy-reactive-problem-integration-tests + Quarkus Resteasy Reactive Problem - Integration Tests + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-reactive-problem + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/main/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResource.java b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/main/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResource.java new file mode 100644 index 00000000000000..6b2414a142218d --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/main/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResource.java @@ -0,0 +1,32 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package io.quarkus.resteasy.reactive.problem.it; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("/quarkus-resteasy-reactive-problem") +@ApplicationScoped +public class QuarkusResteasyReactiveProblemResource { + // add some rest methods here + + @GET + public String hello() { + return "Hello quarkus-resteasy-reactive-problem"; + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/main/resources/application.properties b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/main/resources/application.properties new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/NativeQuarkusResteasyReactiveProblemResourceIT.java b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/NativeQuarkusResteasyReactiveProblemResourceIT.java new file mode 100644 index 00000000000000..d9a13937ccab1b --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/NativeQuarkusResteasyReactiveProblemResourceIT.java @@ -0,0 +1,7 @@ +package io.quarkus.resteasy.reactive.problem.it; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeQuarkusResteasyReactiveProblemResourceIT extends QuarkusResteasyReactiveProblemResourceTest { +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResourceTest.java b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResourceTest.java new file mode 100644 index 00000000000000..d40f4f040afa5a --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/integration-tests/src/test/java/io/quarkus/resteasy/reactive/problem/it/QuarkusResteasyReactiveProblemResourceTest.java @@ -0,0 +1,21 @@ +package io.quarkus.resteasy.reactive.problem.it; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class QuarkusResteasyReactiveProblemResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/quarkus-resteasy-reactive-problem") + .then() + .statusCode(200) + .body(is("Hello quarkus-resteasy-reactive-problem")); + } +} diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/pom.xml b/extensions/panache/quarkus-resteasy-reactive-problem/pom.xml new file mode 100644 index 00000000000000..2e05d96b13c3a6 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + io.quarkus + quarkus-resteasy-reactive-problem-parent + 999-SNAPSHOT + pom + Quarkus Resteasy Reactive Problem - Parent + + deployment + runtime + + + 3.8.1 + ${surefire-plugin.version} + true + 1.8 + 1.8 + UTF-8 + UTF-8 + 999-SNAPSHOT + 3.0.0-M5 + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + + + diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/runtime/pom.xml b/extensions/panache/quarkus-resteasy-reactive-problem/runtime/pom.xml new file mode 100644 index 00000000000000..7f9dc57de0c0e6 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/runtime/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + io.quarkus + quarkus-resteasy-reactive-problem-parent + 999-SNAPSHOT + + quarkus-resteasy-reactive-problem + Quarkus Resteasy Reactive Problem - Runtime + + + io.quarkus + quarkus-arc + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/extensions/panache/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/panache/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..22fb2756cf9cd3 --- /dev/null +++ b/extensions/panache/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Quarkus Resteasy Reactive Problem +#description: Quarkus Resteasy Reactive Problem ... +metadata: +# keywords: +# - quarkus-resteasy-reactive-problem +# guide: ... +# categories: +# - "miscellaneous" +# status: "preview" \ No newline at end of file diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java index 8da00a85419acd..a1db6f02074b96 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java @@ -5,7 +5,6 @@ import org.jboss.resteasy.links.impl.EL; -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.deployment.Capabilities; @@ -29,15 +28,9 @@ import io.quarkus.rest.data.panache.runtime.hal.HalLink; import io.quarkus.rest.data.panache.runtime.hal.HalLinkJacksonSerializer; import io.quarkus.rest.data.panache.runtime.hal.HalLinkJsonbSerializer; -import io.quarkus.rest.data.panache.runtime.jta.TransactionalExecutor; public class RestDataProcessor { - @BuildStep - AdditionalBeanBuildItem registerTransactionalExecutor() { - return AdditionalBeanBuildItem.unremovableOf(TransactionalExecutor.class); - } - @BuildStep void implementResources(CombinedIndexBuildItem index, List resourceBuildItems, List resourcePropertiesBuildItems, Capabilities capabilities, diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java index 3034803a13a5b8..4b0fc57fa50e0b 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java @@ -24,7 +24,7 @@ import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties; import io.quarkus.rest.data.panache.deployment.utils.ResponseImplementor; -import io.quarkus.rest.data.panache.runtime.jta.TransactionalExecutor; +import io.quarkus.rest.data.panache.runtime.UpdateExecutor; public final class UpdateMethodImplementor extends StandardMethodImplementor { @@ -59,7 +59,7 @@ public UpdateMethodImplementor(boolean withValidation) { * ) * public Response update(@PathParam("id") ID id, Entity entityToSave) { * try { - * Object newEntity = transactionalExecutor.execute(() -> { + * Object newEntity = updateExecutor.execute(() -> { * if (resource.get(id) == null) { * return resource.update(id, entityToSave); * } else { @@ -110,15 +110,16 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res ResultHandle id = methodCreator.getMethodParam(0); ResultHandle entityToSave = methodCreator.getMethodParam(1); - // Invoke resource methods inside a supplier function which will be given to a transactional executor to make + // Invoke resource methods inside a supplier function which will be given to an update executor. + // For ORM, this update executor will have the @Transactional annotation to make // sure that all database operations are executed in a single transaction. TryBlock tryBlock = implementTryBlock(methodCreator, "Failed to update an entity"); - ResultHandle transactionalExecutor = getTransactionalExecutor(tryBlock); + ResultHandle updateExecutor = getUpdateExecutor(tryBlock); ResultHandle updateFunction = getUpdateFunction(tryBlock, resourceMetadata.getResourceClass(), resource, id, entityToSave); - ResultHandle newEntity = tryBlock.invokeVirtualMethod( - ofMethod(TransactionalExecutor.class, "execute", Object.class, Supplier.class), - transactionalExecutor, updateFunction); + ResultHandle newEntity = tryBlock.invokeInterfaceMethod( + ofMethod(UpdateExecutor.class, "execute", Object.class, Supplier.class), + updateExecutor, updateFunction); BranchResult createdNewEntity = tryBlock.ifNotNull(newEntity); createdNewEntity.trueBranch() @@ -172,18 +173,18 @@ private void updateAndReturn(BytecodeCreator creator, String resourceClass, Resu creator.returnValue(creator.loadNull()); } - private ResultHandle getTransactionalExecutor(BytecodeCreator creator) { + private ResultHandle getUpdateExecutor(BytecodeCreator creator) { ResultHandle arcContainer = creator.invokeStaticMethod(ofMethod(Arc.class, "container", ArcContainer.class)); ResultHandle instanceHandle = creator.invokeInterfaceMethod( ofMethod(ArcContainer.class, "instance", InstanceHandle.class, Class.class, Annotation[].class), - arcContainer, creator.loadClass(TransactionalExecutor.class), creator.newArray(Annotation.class, 0)); + arcContainer, creator.loadClass(UpdateExecutor.class), creator.newArray(Annotation.class, 0)); ResultHandle instance = creator.invokeInterfaceMethod( ofMethod(InstanceHandle.class, "get", Object.class), instanceHandle); creator.ifNull(instance) .trueBranch() .throwException(RuntimeException.class, - TransactionalExecutor.class.getSimpleName() + " instance was not found"); + UpdateExecutor.class.getSimpleName() + " instance was not found"); return instance; } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java index 6bf420f948d767..a4634dbc85a704 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java @@ -24,7 +24,7 @@ import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties; import io.quarkus.rest.data.panache.deployment.utils.ResponseImplementor; -import io.quarkus.rest.data.panache.runtime.jta.TransactionalExecutor; +import io.quarkus.rest.data.panache.runtime.UpdateExecutor; public final class UpdateHalMethodImplementor extends HalMethodImplementor { @@ -52,7 +52,7 @@ public UpdateHalMethodImplementor(boolean withValidation) { * @Produces({"application/hal+json"}) * public Response updateHal(@PathParam("id") ID id, Entity entityToSave) { * try { - * Object newEntity = transactionalExecutor.execute(() -> { + * Object newEntity = updateExecutor.execute(() -> { * if (resource.get(id) == null) { * return resource.update(id, entityToSave); * } else { @@ -103,15 +103,16 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res ResultHandle id = methodCreator.getMethodParam(0); ResultHandle entityToSave = methodCreator.getMethodParam(1); - // Invoke resource methods inside a supplier function which will be given to a transactional executor to make + // Invoke resource methods inside a supplier function which will be given to an update executor. + // For ORM, this update executor will have the @Transactional annotation to make // sure that all database operations are executed in a single transaction. TryBlock tryBlock = implementTryBlock(methodCreator, "Failed to update an entity"); - ResultHandle transactionalExecutor = getTransactionalExecutor(tryBlock); + ResultHandle updateExecutor = getUpdateExecutor(tryBlock); ResultHandle updateFunction = getUpdateFunction(tryBlock, resourceMetadata.getResourceClass(), resource, id, entityToSave); - ResultHandle newEntity = tryBlock.invokeVirtualMethod( - ofMethod(TransactionalExecutor.class, "execute", Object.class, Supplier.class), - transactionalExecutor, updateFunction); + ResultHandle newEntity = tryBlock.invokeInterfaceMethod( + ofMethod(UpdateExecutor.class, "execute", Object.class, Supplier.class), + updateExecutor, updateFunction); BranchResult createdNewEntity = tryBlock.ifNotNull(newEntity); ResultHandle wrappedNewEntity = wrapHalEntity(createdNewEntity.trueBranch(), newEntity); @@ -167,18 +168,18 @@ private void updateAndReturn(BytecodeCreator creator, String resourceClass, Resu creator.returnValue(creator.loadNull()); } - private ResultHandle getTransactionalExecutor(BytecodeCreator creator) { + private ResultHandle getUpdateExecutor(BytecodeCreator creator) { ResultHandle arcContainer = creator.invokeStaticMethod(ofMethod(Arc.class, "container", ArcContainer.class)); ResultHandle instanceHandle = creator.invokeInterfaceMethod( ofMethod(ArcContainer.class, "instance", InstanceHandle.class, Class.class, Annotation[].class), - arcContainer, creator.loadClass(TransactionalExecutor.class), creator.newArray(Annotation.class, 0)); + arcContainer, creator.loadClass(UpdateExecutor.class), creator.newArray(Annotation.class, 0)); ResultHandle instance = creator.invokeInterfaceMethod( ofMethod(InstanceHandle.class, "get", Object.class), instanceHandle); creator.ifNull(instance) .trueBranch() .throwException(RuntimeException.class, - TransactionalExecutor.class.getSimpleName() + " instance was not found"); + UpdateExecutor.class.getSimpleName() + " instance was not found"); return instance; } diff --git a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/UpdateExecutor.java b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/UpdateExecutor.java new file mode 100644 index 00000000000000..024af563a2fa36 --- /dev/null +++ b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/UpdateExecutor.java @@ -0,0 +1,7 @@ +package io.quarkus.rest.data.panache.runtime; + +import java.util.function.Supplier; + +public interface UpdateExecutor { + T execute(Supplier supplier); +} diff --git a/extensions/picocli/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/picocli/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 8e0578be53ce30..7bc9f799d026c4 100644 --- a/extensions/picocli/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/picocli/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -13,4 +13,4 @@ metadata: languages: - "java" - "kotlin" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/pom.xml b/extensions/pom.xml index 44ff9a58ff8e92..31d17454c6253e 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -36,6 +36,7 @@ websockets webjars-locator resteasy-reactive + apache-httpclient smallrye-health @@ -45,23 +46,11 @@ jaeger micrometer micrometer-registry-prometheus + opentelemetry + opentelemetry-exporter-jaeger - resteasy-common - resteasy-server-common - resteasy - resteasy-jsonb - resteasy-jackson - resteasy-jaxb - resteasy-mutiny-common - resteasy-mutiny - resteasy-qute - resteasy-multipart - rest-client - rest-client-jackson - rest-client-jsonb - rest-client-jaxb - rest-client-mutiny + resteasy-classic smallrye-openapi-common smallrye-openapi swagger-ui diff --git a/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java b/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java index 684d9388c74807..bb806b18e1f69e 100644 --- a/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java +++ b/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java @@ -68,7 +68,7 @@ public class QuartzScheduler implements Scheduler { private final boolean startHalted; public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, SchedulerRuntimeConfig schedulerRuntimeConfig, - Event skippedExecutionEvent, Instance jobs, Instance userTransation) { + Event skippedExecutionEvent, Instance jobs, Instance userTransaction) { enabled = schedulerRuntimeConfig.enabled; final QuartzRuntimeConfig runtimeConfig = quartzSupport.getRuntimeConfig(); warnDeprecated(runtimeConfig); @@ -95,8 +95,8 @@ public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Sc try { boolean manageTx = quartzSupport.getBuildTimeConfig().storeType.isNonManagedTxJobStore(); - if (manageTx && userTransation.isResolvable()) { - transaction = userTransation.get(); + if (manageTx && userTransaction.isResolvable()) { + transaction = userTransaction.get(); } Properties props = getSchedulerConfigurationProperties(quartzSupport); @@ -281,10 +281,11 @@ void start(@Observes @Priority(Interceptor.Priority.PLATFORM_BEFORE) StartupEven * * @param event ignored */ - void destroy(@BeforeDestroyed(ApplicationScoped.class) Object event) { // + void destroy(@Observes @BeforeDestroyed(ApplicationScoped.class) Object event) { if (scheduler != null) { try { - scheduler.shutdown(true); // gracefully shutdown + // Note that this method does not return until all currently executing jobs have completed + scheduler.shutdown(true); } catch (SchedulerException e) { LOGGER.warnf("Unable to gracefully shutdown the scheduler", e); } diff --git a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java index 319d51f966a1cb..8937c188094857 100644 --- a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java +++ b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java @@ -74,8 +74,15 @@ HealthBuildItem addHealthCheck(RedisBuildTimeConfig buildTimeConfig) { } @BuildStep - RuntimeInitializedClassBuildItem initializeBulkTypeDuringRuntime() { - return new RuntimeInitializedClassBuildItem(BulkType.class.getName()); + public void registerRuntimeInitializedClasses(BuildProducer producer) { + producer.produce(new RuntimeInitializedClassBuildItem(BulkType.class.getName())); + // Classes using SplittableRandom, which need to be runtime initialized + producer.produce(new RuntimeInitializedClassBuildItem("io.vertx.redis.client.impl.RedisSentinelClient")); + producer.produce(new RuntimeInitializedClassBuildItem("io.vertx.redis.client.impl.Slots")); + producer.produce(new RuntimeInitializedClassBuildItem("io.vertx.redis.client.impl.RedisClusterConnection")); + // RedisClusterConnections is referenced from RedisClusterClient. Thus, we need to runtime-init + // that too. + producer.produce(new RuntimeInitializedClassBuildItem("io.vertx.redis.client.impl.RedisClusterClient")); } @BuildStep diff --git a/extensions/resteasy-classic/pom.xml b/extensions/resteasy-classic/pom.xml new file mode 100644 index 00000000000000..14bd84a28fad9a --- /dev/null +++ b/extensions/resteasy-classic/pom.xml @@ -0,0 +1,34 @@ + + + + quarkus-extensions-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-resteasy-parent-aggregator + Quarkus - RESTEasy - Parent - Aggregator + pom + + resteasy-common + resteasy-server-common + resteasy + resteasy-jsonb + resteasy-jackson + resteasy-jaxb + resteasy-mutiny-common + resteasy-mutiny + resteasy-qute + resteasy-multipart + rest-client + rest-client-jackson + rest-client-jsonb + rest-client-jaxb + rest-client-mutiny + + + diff --git a/extensions/rest-client-jackson/deployment/pom.xml b/extensions/resteasy-classic/rest-client-jackson/deployment/pom.xml similarity index 100% rename from extensions/rest-client-jackson/deployment/pom.xml rename to extensions/resteasy-classic/rest-client-jackson/deployment/pom.xml diff --git a/extensions/rest-client-jackson/deployment/src/main/java/io/quarkus/restclient/jackson/deployment/RestClientJacksonProcessor.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/main/java/io/quarkus/restclient/jackson/deployment/RestClientJacksonProcessor.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/main/java/io/quarkus/restclient/jackson/deployment/RestClientJacksonProcessor.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/main/java/io/quarkus/restclient/jackson/deployment/RestClientJacksonProcessor.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ClientResource.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ClientResource.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ClientResource.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ClientResource.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/DateDto.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/DateDto.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/DateDto.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/DateDto.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/HelloResource.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/HelloResource.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/HelloResource.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/HelloResource.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/JacksonRestClientTest.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/JacksonRestClientTest.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/JacksonRestClientTest.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/JacksonRestClientTest.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/RestInterface.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/RestInterface.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/RestInterface.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/RestInterface.java diff --git a/extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ZonedDateTimeObjectMapperCustomizer.java b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ZonedDateTimeObjectMapperCustomizer.java similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ZonedDateTimeObjectMapperCustomizer.java rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/java/io/quarkus/restclient/jackson/deployment/ZonedDateTimeObjectMapperCustomizer.java diff --git a/extensions/rest-client-jackson/deployment/src/test/resources/application.properties b/extensions/resteasy-classic/rest-client-jackson/deployment/src/test/resources/application.properties similarity index 100% rename from extensions/rest-client-jackson/deployment/src/test/resources/application.properties rename to extensions/resteasy-classic/rest-client-jackson/deployment/src/test/resources/application.properties diff --git a/extensions/rest-client-jackson/pom.xml b/extensions/resteasy-classic/rest-client-jackson/pom.xml similarity index 91% rename from extensions/rest-client-jackson/pom.xml rename to extensions/resteasy-classic/rest-client-jackson/pom.xml index bed008174c6c24..7ea707d42917f1 100644 --- a/extensions/rest-client-jackson/pom.xml +++ b/extensions/resteasy-classic/rest-client-jackson/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/rest-client-jackson/runtime/pom.xml b/extensions/resteasy-classic/rest-client-jackson/runtime/pom.xml similarity index 96% rename from extensions/rest-client-jackson/runtime/pom.xml rename to extensions/resteasy-classic/rest-client-jackson/runtime/pom.xml index 2ff76a57df9284..5567dc6af511d7 100644 --- a/extensions/rest-client-jackson/runtime/pom.xml +++ b/extensions/resteasy-classic/rest-client-jackson/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-rest-client-jackson Quarkus - REST Client - Jackson - Runtime - Enable Jackson serialization for the REST Client + Jackson serialization support for the REST Client io.quarkus diff --git a/extensions/rest-client-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/rest-client-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/rest-client-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/rest-client-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/rest-client-jaxb/deployment/pom.xml b/extensions/resteasy-classic/rest-client-jaxb/deployment/pom.xml similarity index 100% rename from extensions/rest-client-jaxb/deployment/pom.xml rename to extensions/resteasy-classic/rest-client-jaxb/deployment/pom.xml diff --git a/extensions/rest-client-jaxb/deployment/src/main/java/io/quarkus/restclient/jaxb/deployment/RestClientJaxbProcessor.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/main/java/io/quarkus/restclient/jaxb/deployment/RestClientJaxbProcessor.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/main/java/io/quarkus/restclient/jaxb/deployment/RestClientJaxbProcessor.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/main/java/io/quarkus/restclient/jaxb/deployment/RestClientJaxbProcessor.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/Book.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/Book.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/Book.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/Book.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/ClientResource.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/ClientResource.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/ClientResource.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/ClientResource.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/HelloResource.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/HelloResource.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/HelloResource.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/HelloResource.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/JaxbRestClientTest.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/JaxbRestClientTest.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/JaxbRestClientTest.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/JaxbRestClientTest.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/RestInterface.java b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/RestInterface.java similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/RestInterface.java rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/java/io/quarkus/restclient/jaxb/deployment/RestInterface.java diff --git a/extensions/rest-client-jaxb/deployment/src/test/resources/application.properties b/extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/resources/application.properties similarity index 100% rename from extensions/rest-client-jaxb/deployment/src/test/resources/application.properties rename to extensions/resteasy-classic/rest-client-jaxb/deployment/src/test/resources/application.properties diff --git a/extensions/rest-client-jaxb/pom.xml b/extensions/resteasy-classic/rest-client-jaxb/pom.xml similarity index 91% rename from extensions/rest-client-jaxb/pom.xml rename to extensions/resteasy-classic/rest-client-jaxb/pom.xml index 26c75be023b2b6..24064e5b91b5d8 100644 --- a/extensions/rest-client-jaxb/pom.xml +++ b/extensions/resteasy-classic/rest-client-jaxb/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/rest-client-jaxb/runtime/pom.xml b/extensions/resteasy-classic/rest-client-jaxb/runtime/pom.xml similarity index 96% rename from extensions/rest-client-jaxb/runtime/pom.xml rename to extensions/resteasy-classic/rest-client-jaxb/runtime/pom.xml index 4bbf4ef2299f4f..9e61aa70e3c704 100644 --- a/extensions/rest-client-jaxb/runtime/pom.xml +++ b/extensions/resteasy-classic/rest-client-jaxb/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-rest-client-jaxb Quarkus - REST Client - JAXB - Runtime - Enable XML serialization for the REST Client + XML serialization support for the REST Client io.quarkus diff --git a/extensions/rest-client-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/rest-client-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/rest-client-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/rest-client-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/rest-client-jsonb/deployment/pom.xml b/extensions/resteasy-classic/rest-client-jsonb/deployment/pom.xml similarity index 100% rename from extensions/rest-client-jsonb/deployment/pom.xml rename to extensions/resteasy-classic/rest-client-jsonb/deployment/pom.xml diff --git a/extensions/rest-client-jsonb/deployment/src/main/java/io/quarkus/restclient/jsonb/deployment/RestClientJsonbProcessor.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/main/java/io/quarkus/restclient/jsonb/deployment/RestClientJsonbProcessor.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/main/java/io/quarkus/restclient/jsonb/deployment/RestClientJsonbProcessor.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/main/java/io/quarkus/restclient/jsonb/deployment/RestClientJsonbProcessor.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ClientResource.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ClientResource.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ClientResource.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ClientResource.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/DateDto.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/DateDto.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/DateDto.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/DateDto.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/HelloResource.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/HelloResource.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/HelloResource.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/HelloResource.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/JsonbRestClientTest.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/JsonbRestClientTest.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/JsonbRestClientTest.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/JsonbRestClientTest.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/RestInterface.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/RestInterface.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/RestInterface.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/RestInterface.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ZonedDateTimeJsonbConfigCustomizer.java b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ZonedDateTimeJsonbConfigCustomizer.java similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ZonedDateTimeJsonbConfigCustomizer.java rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/java/io/quarkus/restclient/jsonb/deployment/ZonedDateTimeJsonbConfigCustomizer.java diff --git a/extensions/rest-client-jsonb/deployment/src/test/resources/application.properties b/extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/resources/application.properties similarity index 100% rename from extensions/rest-client-jsonb/deployment/src/test/resources/application.properties rename to extensions/resteasy-classic/rest-client-jsonb/deployment/src/test/resources/application.properties diff --git a/extensions/rest-client-jsonb/pom.xml b/extensions/resteasy-classic/rest-client-jsonb/pom.xml similarity index 91% rename from extensions/rest-client-jsonb/pom.xml rename to extensions/resteasy-classic/rest-client-jsonb/pom.xml index 02d92982456d26..db239408a1b544 100644 --- a/extensions/rest-client-jsonb/pom.xml +++ b/extensions/resteasy-classic/rest-client-jsonb/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/rest-client-jsonb/runtime/pom.xml b/extensions/resteasy-classic/rest-client-jsonb/runtime/pom.xml similarity index 96% rename from extensions/rest-client-jsonb/runtime/pom.xml rename to extensions/resteasy-classic/rest-client-jsonb/runtime/pom.xml index f459709bd05fe0..2187cc22452adc 100644 --- a/extensions/rest-client-jsonb/runtime/pom.xml +++ b/extensions/resteasy-classic/rest-client-jsonb/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-rest-client-jsonb Quarkus - REST Client - JSON-B - Runtime - Enable JSON-B serialization for the REST client + JSON-B serialization support for the REST client diff --git a/extensions/rest-client-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/rest-client-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/rest-client-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/rest-client-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/rest-client-mutiny/deployment/pom.xml b/extensions/resteasy-classic/rest-client-mutiny/deployment/pom.xml similarity index 100% rename from extensions/rest-client-mutiny/deployment/pom.xml rename to extensions/resteasy-classic/rest-client-mutiny/deployment/pom.xml diff --git a/extensions/rest-client-mutiny/deployment/src/main/java/io/quarkus/restclient/mutiny/deployment/RestClientMutinyProcessor.java b/extensions/resteasy-classic/rest-client-mutiny/deployment/src/main/java/io/quarkus/restclient/mutiny/deployment/RestClientMutinyProcessor.java similarity index 100% rename from extensions/rest-client-mutiny/deployment/src/main/java/io/quarkus/restclient/mutiny/deployment/RestClientMutinyProcessor.java rename to extensions/resteasy-classic/rest-client-mutiny/deployment/src/main/java/io/quarkus/restclient/mutiny/deployment/RestClientMutinyProcessor.java diff --git a/extensions/rest-client-mutiny/deployment/src/test/java/io/quarkus/restclient/mutiny/deployment/MutinyRestClientTest.java b/extensions/resteasy-classic/rest-client-mutiny/deployment/src/test/java/io/quarkus/restclient/mutiny/deployment/MutinyRestClientTest.java similarity index 100% rename from extensions/rest-client-mutiny/deployment/src/test/java/io/quarkus/restclient/mutiny/deployment/MutinyRestClientTest.java rename to extensions/resteasy-classic/rest-client-mutiny/deployment/src/test/java/io/quarkus/restclient/mutiny/deployment/MutinyRestClientTest.java diff --git a/extensions/rest-client-mutiny/pom.xml b/extensions/resteasy-classic/rest-client-mutiny/pom.xml similarity index 91% rename from extensions/rest-client-mutiny/pom.xml rename to extensions/resteasy-classic/rest-client-mutiny/pom.xml index 46a098e9c9e3ba..1a564b5e275c4a 100644 --- a/extensions/rest-client-mutiny/pom.xml +++ b/extensions/resteasy-classic/rest-client-mutiny/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/rest-client-mutiny/runtime/pom.xml b/extensions/resteasy-classic/rest-client-mutiny/runtime/pom.xml similarity index 100% rename from extensions/rest-client-mutiny/runtime/pom.xml rename to extensions/resteasy-classic/rest-client-mutiny/runtime/pom.xml diff --git a/extensions/rest-client-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/rest-client-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/rest-client-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/rest-client-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/rest-client/deployment/pom.xml b/extensions/resteasy-classic/rest-client/deployment/pom.xml similarity index 93% rename from extensions/rest-client/deployment/pom.xml rename to extensions/resteasy-classic/rest-client/deployment/pom.xml index 9b07466bc28958..055b9a26d2bc13 100644 --- a/extensions/rest-client/deployment/pom.xml +++ b/extensions/resteasy-classic/rest-client/deployment/pom.xml @@ -25,6 +25,10 @@ io.quarkus quarkus-resteasy-common-deployment + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-rest-client diff --git a/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientAnnotationProviderBuildItem.java b/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientAnnotationProviderBuildItem.java similarity index 100% rename from extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientAnnotationProviderBuildItem.java rename to extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientAnnotationProviderBuildItem.java diff --git a/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java b/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java similarity index 98% rename from extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java rename to extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java index 41407fe7914572..3027492a64b17d 100644 --- a/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java +++ b/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java @@ -134,6 +134,12 @@ void registerRestClientListenerForTracing( "META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener")); reflectiveClass .produce(new ReflectiveClassBuildItem(true, true, "io.smallrye.opentracing.SmallRyeRestClientListener")); + } else if (capabilities.isPresent(Capability.OPENTELEMETRY_TRACER)) { + resource.produce(new NativeImageResourceBuildItem( + "META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener")); + reflectiveClass + .produce(new ReflectiveClassBuildItem(true, true, + "io.quarkus.opentelemetry.tracing.client.QuarkusRestClientListener")); } } diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceClient.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceClient.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceClient.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceClient.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceRedirectEndpoint.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceRedirectEndpoint.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceRedirectEndpoint.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceRedirectEndpoint.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceUnavailableEndpoint.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceUnavailableEndpoint.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceUnavailableEndpoint.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/DownstreamServiceUnavailableEndpoint.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/FrontendService.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/FrontendService.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/FrontendService.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/FrontendService.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionOriginalBehaviorTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionOriginalBehaviorTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionOriginalBehaviorTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionOriginalBehaviorTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/exception/RestClientExceptionTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/AsyncRestClientFallbackTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/AsyncRestClientFallbackTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/AsyncRestClientFallbackTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/AsyncRestClientFallbackTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/RestClientFallbackTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/RestClientFallbackTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/RestClientFallbackTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/ft/RestClientFallbackTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoClient.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoClient.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoClient.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoClient.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoResource.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoResource.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoResource.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/EchoResource.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/MyHeadersFactory.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/MyHeadersFactory.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/MyHeadersFactory.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/MyHeadersFactory.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/RegisterClientHeadersTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/RegisterClientHeadersTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/RegisterClientHeadersTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerclientheaders/RegisterClientHeadersTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoClient.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoClient.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoClient.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoClient.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoResource.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoResource.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoResource.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/EchoResource.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MethodsCollector.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MethodsCollector.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MethodsCollector.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MethodsCollector.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyFilter.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyFilter.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyFilter.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyFilter.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyRequestBean.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyRequestBean.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyRequestBean.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/MyRequestBean.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/ProviderClientRegistrationTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/ProviderClientRegistrationTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/ProviderClientRegistrationTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/ProviderClientRegistrationTest.java diff --git a/extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/RegisterProviderTest.java b/extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/RegisterProviderTest.java similarity index 100% rename from extensions/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/RegisterProviderTest.java rename to extensions/resteasy-classic/rest-client/deployment/src/test/java/io/quarkus/restclient/registerprovider/RegisterProviderTest.java diff --git a/extensions/rest-client/pom.xml b/extensions/resteasy-classic/rest-client/pom.xml similarity index 91% rename from extensions/rest-client/pom.xml rename to extensions/resteasy-classic/rest-client/pom.xml index 28073c0276a2f2..c44614c9b2b140 100644 --- a/extensions/rest-client/pom.xml +++ b/extensions/resteasy-classic/rest-client/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/rest-client/runtime/pom.xml b/extensions/resteasy-classic/rest-client/runtime/pom.xml similarity index 95% rename from extensions/rest-client/runtime/pom.xml rename to extensions/resteasy-classic/rest-client/runtime/pom.xml index cc85da855b8fa5..cab37f9ea57de4 100644 --- a/extensions/rest-client/runtime/pom.xml +++ b/extensions/resteasy-classic/rest-client/runtime/pom.xml @@ -26,6 +26,10 @@ io.quarkus quarkus-resteasy-common + + io.quarkus + quarkus-apache-httpclient + org.jboss.resteasy resteasy-client-microprofile diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/NoopHostnameVerifier.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/NoopHostnameVerifier.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/NoopHostnameVerifier.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/NoopHostnameVerifier.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/BuilderResolver.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/BuilderResolver.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/BuilderResolver.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/BuilderResolver.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientBuilderReplacement.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientBuilderReplacement.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientBuilderReplacement.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientBuilderReplacement.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientHttpEngineBuilder43Replacement.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientHttpEngineBuilder43Replacement.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientHttpEngineBuilder43Replacement.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ClientHttpEngineBuilder43Replacement.java diff --git a/extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ComputedHeaderValueFillerReplacement.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ComputedHeaderValueFillerReplacement.java similarity index 100% rename from extensions/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ComputedHeaderValueFillerReplacement.java rename to extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/graal/ComputedHeaderValueFillerReplacement.java diff --git a/extensions/rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-common/deployment/pom.xml b/extensions/resteasy-classic/resteasy-common/deployment/pom.xml similarity index 100% rename from extensions/resteasy-common/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-common/deployment/pom.xml diff --git a/extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsProvidersToRegisterBuildItem.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsProvidersToRegisterBuildItem.java similarity index 100% rename from extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsProvidersToRegisterBuildItem.java rename to extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsProvidersToRegisterBuildItem.java diff --git a/extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java similarity index 100% rename from extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java rename to extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java diff --git a/extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyCommonProcessor.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyCommonProcessor.java similarity index 100% rename from extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyCommonProcessor.java rename to extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyCommonProcessor.java diff --git a/extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyInjectionReadyBuildItem.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyInjectionReadyBuildItem.java similarity index 100% rename from extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyInjectionReadyBuildItem.java rename to extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyInjectionReadyBuildItem.java diff --git a/extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyJsonConfig.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyJsonConfig.java similarity index 100% rename from extensions/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyJsonConfig.java rename to extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/ResteasyJsonConfig.java diff --git a/extensions/resteasy-common/pom.xml b/extensions/resteasy-classic/resteasy-common/pom.xml similarity index 91% rename from extensions/resteasy-common/pom.xml rename to extensions/resteasy-classic/resteasy-common/pom.xml index 63d6c11cd7eccd..447c9df2412786 100644 --- a/extensions/resteasy-common/pom.xml +++ b/extensions/resteasy-classic/resteasy-common/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-common/runtime/pom.xml b/extensions/resteasy-classic/resteasy-common/runtime/pom.xml similarity index 100% rename from extensions/resteasy-common/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-common/runtime/pom.xml diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusConstructorInjector.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusConstructorInjector.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusConstructorInjector.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusConstructorInjector.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyContextProvider.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyContextProvider.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyContextProvider.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyContextProvider.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyInjectorFactoryRecorder.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyInjectorFactoryRecorder.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyInjectorFactoryRecorder.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/ResteasyInjectorFactoryRecorder.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteDocumentProvider.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteDocumentProvider.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteDocumentProvider.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteDocumentProvider.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProvider.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProvider.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProvider.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProvider.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProviderHelper.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProviderHelper.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProviderHelper.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/graal/DeleteIIOImageProviderHelper.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusJacksonSerializer.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusJacksonSerializer.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusJacksonSerializer.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusJacksonSerializer.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusObjectMapperContextResolver.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusObjectMapperContextResolver.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusObjectMapperContextResolver.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jackson/QuarkusObjectMapperContextResolver.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbContextResolver.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbContextResolver.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbContextResolver.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbContextResolver.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbSerializer.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbSerializer.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbSerializer.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/jsonb/QuarkusJsonbSerializer.java diff --git a/extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/providers/ServerFormUrlEncodedProvider.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/providers/ServerFormUrlEncodedProvider.java similarity index 100% rename from extensions/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/providers/ServerFormUrlEncodedProvider.java rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/providers/ServerFormUrlEncodedProvider.java diff --git a/extensions/resteasy-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-common/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider b/extensions/resteasy-classic/resteasy-common/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider similarity index 100% rename from extensions/resteasy-common/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider rename to extensions/resteasy-classic/resteasy-common/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider diff --git a/extensions/resteasy-common/spi/pom.xml b/extensions/resteasy-classic/resteasy-common/spi/pom.xml similarity index 100% rename from extensions/resteasy-common/spi/pom.xml rename to extensions/resteasy-classic/resteasy-common/spi/pom.xml diff --git a/extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyConfigBuildItem.java b/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyConfigBuildItem.java similarity index 100% rename from extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyConfigBuildItem.java rename to extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyConfigBuildItem.java diff --git a/extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java b/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java similarity index 100% rename from extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java rename to extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java diff --git a/extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyJaxrsProviderBuildItem.java b/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyJaxrsProviderBuildItem.java similarity index 100% rename from extensions/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyJaxrsProviderBuildItem.java rename to extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyJaxrsProviderBuildItem.java diff --git a/extensions/resteasy-jackson/deployment/pom.xml b/extensions/resteasy-classic/resteasy-jackson/deployment/pom.xml similarity index 100% rename from extensions/resteasy-jackson/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-jackson/deployment/pom.xml diff --git a/extensions/resteasy-jackson/deployment/src/main/java/io/quarkus/resteasy/jackson/deployment/ResteasyJacksonProcessor.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/main/java/io/quarkus/resteasy/jackson/deployment/ResteasyJacksonProcessor.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/main/java/io/quarkus/resteasy/jackson/deployment/ResteasyJacksonProcessor.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/main/java/io/quarkus/resteasy/jackson/deployment/ResteasyJacksonProcessor.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/DateDto.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/DateDto.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/DateDto.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/DateDto.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloNoMediaTypeResource.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloNoMediaTypeResource.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloNoMediaTypeResource.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloNoMediaTypeResource.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloResource.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloResource.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloResource.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/HelloResource.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/Message.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/Message.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/Message.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/Message.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/MultipleTimeModuleTest.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/MultipleTimeModuleTest.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/MultipleTimeModuleTest.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/MultipleTimeModuleTest.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/NoMediaTypeTest.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/NoMediaTypeTest.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/NoMediaTypeTest.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/NoMediaTypeTest.java diff --git a/extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/TimeCustomizer.java b/extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/TimeCustomizer.java similarity index 100% rename from extensions/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/TimeCustomizer.java rename to extensions/resteasy-classic/resteasy-jackson/deployment/src/test/java/io/quarkus/resteasy/jackson/TimeCustomizer.java diff --git a/extensions/resteasy-jackson/pom.xml b/extensions/resteasy-classic/resteasy-jackson/pom.xml similarity index 91% rename from extensions/resteasy-jackson/pom.xml rename to extensions/resteasy-classic/resteasy-jackson/pom.xml index fe4c5c14b193cf..15736d5e4192d7 100644 --- a/extensions/resteasy-jackson/pom.xml +++ b/extensions/resteasy-classic/resteasy-jackson/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-jackson/runtime/pom.xml b/extensions/resteasy-classic/resteasy-jackson/runtime/pom.xml similarity index 100% rename from extensions/resteasy-jackson/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-jackson/runtime/pom.xml diff --git a/extensions/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 82% rename from extensions/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 82e96447cc9d02..b5c836c5a9080d 100644 --- a/extensions/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/resteasy-classic/resteasy-jackson/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -18,4 +18,4 @@ metadata: languages: - "java" - "kotlin" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/resteasy-jaxb/deployment/pom.xml b/extensions/resteasy-classic/resteasy-jaxb/deployment/pom.xml similarity index 100% rename from extensions/resteasy-jaxb/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-jaxb/deployment/pom.xml diff --git a/extensions/resteasy-jaxb/deployment/src/main/java/io/quarkus/resteasy/jaxb/deployment/ResteasyJaxbProcessor.java b/extensions/resteasy-classic/resteasy-jaxb/deployment/src/main/java/io/quarkus/resteasy/jaxb/deployment/ResteasyJaxbProcessor.java similarity index 100% rename from extensions/resteasy-jaxb/deployment/src/main/java/io/quarkus/resteasy/jaxb/deployment/ResteasyJaxbProcessor.java rename to extensions/resteasy-classic/resteasy-jaxb/deployment/src/main/java/io/quarkus/resteasy/jaxb/deployment/ResteasyJaxbProcessor.java diff --git a/extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/Bar.java b/extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/Bar.java similarity index 100% rename from extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/Bar.java rename to extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/Bar.java diff --git a/extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ConsumesXMLTestCase.java b/extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ConsumesXMLTestCase.java similarity index 100% rename from extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ConsumesXMLTestCase.java rename to extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ConsumesXMLTestCase.java diff --git a/extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ProducesXMLTestCase.java b/extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ProducesXMLTestCase.java similarity index 100% rename from extensions/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ProducesXMLTestCase.java rename to extensions/resteasy-classic/resteasy-jaxb/deployment/src/test/java/io/quarkus/resteasy/jaxb/deployment/ProducesXMLTestCase.java diff --git a/extensions/resteasy-jaxb/pom.xml b/extensions/resteasy-classic/resteasy-jaxb/pom.xml similarity index 91% rename from extensions/resteasy-jaxb/pom.xml rename to extensions/resteasy-classic/resteasy-jaxb/pom.xml index 3d3726599a3985..28202cca11a6de 100644 --- a/extensions/resteasy-jaxb/pom.xml +++ b/extensions/resteasy-classic/resteasy-jaxb/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-jaxb/runtime/pom.xml b/extensions/resteasy-classic/resteasy-jaxb/runtime/pom.xml similarity index 100% rename from extensions/resteasy-jaxb/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-jaxb/runtime/pom.xml diff --git a/extensions/resteasy-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-jaxb/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-jsonb/deployment/pom.xml b/extensions/resteasy-classic/resteasy-jsonb/deployment/pom.xml similarity index 100% rename from extensions/resteasy-jsonb/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-jsonb/deployment/pom.xml diff --git a/extensions/resteasy-jsonb/deployment/src/main/java/io/quarkus/resteasy/jsonb/deployment/ResteasyJsonbProcessor.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/main/java/io/quarkus/resteasy/jsonb/deployment/ResteasyJsonbProcessor.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/main/java/io/quarkus/resteasy/jsonb/deployment/ResteasyJsonbProcessor.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/main/java/io/quarkus/resteasy/jsonb/deployment/ResteasyJsonbProcessor.java diff --git a/extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/HelloNoMediaTypeResource.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/HelloNoMediaTypeResource.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/HelloNoMediaTypeResource.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/HelloNoMediaTypeResource.java diff --git a/extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/Message.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/Message.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/Message.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/Message.java diff --git a/extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/NoMediaTypeTest.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/NoMediaTypeTest.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/NoMediaTypeTest.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/NoMediaTypeTest.java diff --git a/extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/ResourceSendingJsonObjects.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/ResourceSendingJsonObjects.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/ResourceSendingJsonObjects.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/ResourceSendingJsonObjects.java diff --git a/extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/VertxSerializerRegistrationTest.java b/extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/VertxSerializerRegistrationTest.java similarity index 100% rename from extensions/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/VertxSerializerRegistrationTest.java rename to extensions/resteasy-classic/resteasy-jsonb/deployment/src/test/java/io/quarkus/resteasy/jsonb/VertxSerializerRegistrationTest.java diff --git a/extensions/resteasy-jsonb/pom.xml b/extensions/resteasy-classic/resteasy-jsonb/pom.xml similarity index 91% rename from extensions/resteasy-jsonb/pom.xml rename to extensions/resteasy-classic/resteasy-jsonb/pom.xml index 282ef6193f2b45..75764a05a65de9 100644 --- a/extensions/resteasy-jsonb/pom.xml +++ b/extensions/resteasy-classic/resteasy-jsonb/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-jsonb/runtime/pom.xml b/extensions/resteasy-classic/resteasy-jsonb/runtime/pom.xml similarity index 100% rename from extensions/resteasy-jsonb/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-jsonb/runtime/pom.xml diff --git a/extensions/resteasy-jsonb/runtime/src/main/java/io/quarkus/resteasy/jsonb/vertx/VertxJson.java b/extensions/resteasy-classic/resteasy-jsonb/runtime/src/main/java/io/quarkus/resteasy/jsonb/vertx/VertxJson.java similarity index 100% rename from extensions/resteasy-jsonb/runtime/src/main/java/io/quarkus/resteasy/jsonb/vertx/VertxJson.java rename to extensions/resteasy-classic/resteasy-jsonb/runtime/src/main/java/io/quarkus/resteasy/jsonb/vertx/VertxJson.java diff --git a/extensions/resteasy-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-jsonb/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-jsonb/runtime/src/test/java/io/quarkus/resteasy/jsonb/vertx/VertxJsonTest.java b/extensions/resteasy-classic/resteasy-jsonb/runtime/src/test/java/io/quarkus/resteasy/jsonb/vertx/VertxJsonTest.java similarity index 100% rename from extensions/resteasy-jsonb/runtime/src/test/java/io/quarkus/resteasy/jsonb/vertx/VertxJsonTest.java rename to extensions/resteasy-classic/resteasy-jsonb/runtime/src/test/java/io/quarkus/resteasy/jsonb/vertx/VertxJsonTest.java diff --git a/extensions/resteasy-multipart/deployment/pom.xml b/extensions/resteasy-classic/resteasy-multipart/deployment/pom.xml similarity index 100% rename from extensions/resteasy-multipart/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-multipart/deployment/pom.xml diff --git a/extensions/resteasy-multipart/deployment/src/main/java/io/quarkus/resteasy/multipart/deployment/ResteasyMultipartProcessor.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/main/java/io/quarkus/resteasy/multipart/deployment/ResteasyMultipartProcessor.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/main/java/io/quarkus/resteasy/multipart/deployment/ResteasyMultipartProcessor.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/main/java/io/quarkus/resteasy/multipart/deployment/ResteasyMultipartProcessor.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackBody.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackBody.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackBody.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackBody.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackResource.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackResource.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackResource.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/FeedbackResource.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/InvalidEncodingTest.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/InvalidEncodingTest.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/InvalidEncodingTest.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/InvalidEncodingTest.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/MultipartEncodingTest.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/MultipartEncodingTest.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/MultipartEncodingTest.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/MultipartEncodingTest.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeDto.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeDto.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeDto.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeDto.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeEnum.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeEnum.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeEnum.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeEnum.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeMultipartTest.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeMultipartTest.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeMultipartTest.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeMultipartTest.java diff --git a/extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeResource.java b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeResource.java similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeResource.java rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/java/io/quarkus/resteasy/multipart/parttype/PartTypeResource.java diff --git a/extensions/resteasy-multipart/deployment/src/test/resources/application-charset-us-ascii.properties b/extensions/resteasy-classic/resteasy-multipart/deployment/src/test/resources/application-charset-us-ascii.properties similarity index 100% rename from extensions/resteasy-multipart/deployment/src/test/resources/application-charset-us-ascii.properties rename to extensions/resteasy-classic/resteasy-multipart/deployment/src/test/resources/application-charset-us-ascii.properties diff --git a/extensions/resteasy-multipart/pom.xml b/extensions/resteasy-classic/resteasy-multipart/pom.xml similarity index 91% rename from extensions/resteasy-multipart/pom.xml rename to extensions/resteasy-classic/resteasy-multipart/pom.xml index c8578da906a7a4..323ecdfad89879 100644 --- a/extensions/resteasy-multipart/pom.xml +++ b/extensions/resteasy-classic/resteasy-multipart/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-multipart/runtime/pom.xml b/extensions/resteasy-classic/resteasy-multipart/runtime/pom.xml similarity index 89% rename from extensions/resteasy-multipart/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-multipart/runtime/pom.xml index 0d61c4a4103f01..afbb551bbac231 100644 --- a/extensions/resteasy-multipart/runtime/pom.xml +++ b/extensions/resteasy-classic/resteasy-multipart/runtime/pom.xml @@ -25,6 +25,11 @@ commons-logging commons-logging + + + org.jboss.resteasy + resteasy-client + diff --git a/extensions/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/MultipartInputPartConfigContainerRequestFilter.java b/extensions/resteasy-classic/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/MultipartInputPartConfigContainerRequestFilter.java similarity index 100% rename from extensions/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/MultipartInputPartConfigContainerRequestFilter.java rename to extensions/resteasy-classic/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/MultipartInputPartConfigContainerRequestFilter.java diff --git a/extensions/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/ResteasyMultipartRuntimeConfig.java b/extensions/resteasy-classic/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/ResteasyMultipartRuntimeConfig.java similarity index 100% rename from extensions/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/ResteasyMultipartRuntimeConfig.java rename to extensions/resteasy-classic/resteasy-multipart/runtime/src/main/java/io/quarkus/resteasy/multipart/runtime/ResteasyMultipartRuntimeConfig.java diff --git a/extensions/resteasy-multipart/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-multipart/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-multipart/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-multipart/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-multipart/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers b/extensions/resteasy-classic/resteasy-multipart/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers similarity index 100% rename from extensions/resteasy-multipart/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers rename to extensions/resteasy-classic/resteasy-multipart/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers diff --git a/extensions/resteasy-mutiny-common/deployment/pom.xml b/extensions/resteasy-classic/resteasy-mutiny-common/deployment/pom.xml similarity index 87% rename from extensions/resteasy-mutiny-common/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny-common/deployment/pom.xml index 005a9730fd8ae8..1c7698e3a2ec73 100644 --- a/extensions/resteasy-mutiny-common/deployment/pom.xml +++ b/extensions/resteasy-classic/resteasy-mutiny-common/deployment/pom.xml @@ -21,6 +21,11 @@ io.quarkus quarkus-mutiny-deployment + + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-resteasy-mutiny-common diff --git a/extensions/resteasy-mutiny-common/deployment/src/main/java/io/quarkus/resteasy/mutiny/common/deployment/ResteasyMutinyCommonProcessor.java b/extensions/resteasy-classic/resteasy-mutiny-common/deployment/src/main/java/io/quarkus/resteasy/mutiny/common/deployment/ResteasyMutinyCommonProcessor.java similarity index 100% rename from extensions/resteasy-mutiny-common/deployment/src/main/java/io/quarkus/resteasy/mutiny/common/deployment/ResteasyMutinyCommonProcessor.java rename to extensions/resteasy-classic/resteasy-mutiny-common/deployment/src/main/java/io/quarkus/resteasy/mutiny/common/deployment/ResteasyMutinyCommonProcessor.java diff --git a/extensions/resteasy-mutiny-common/pom.xml b/extensions/resteasy-classic/resteasy-mutiny-common/pom.xml similarity index 91% rename from extensions/resteasy-mutiny-common/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny-common/pom.xml index 112c7d4b709511..404c8e6499116f 100644 --- a/extensions/resteasy-mutiny-common/pom.xml +++ b/extensions/resteasy-classic/resteasy-mutiny-common/pom.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-mutiny-common/runtime/pom.xml b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/pom.xml similarity index 91% rename from extensions/resteasy-mutiny-common/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/pom.xml index 3d596c9f0c80eb..cff98bb4946a3c 100644 --- a/extensions/resteasy-mutiny-common/runtime/pom.xml +++ b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/pom.xml @@ -32,6 +32,11 @@ + + + io.quarkus + quarkus-apache-httpclient + io.quarkus quarkus-junit5-internal diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiInvokerProvider.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiInvokerProvider.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiInvokerProvider.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiInvokerProvider.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiProvider.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiProvider.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiProvider.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiProvider.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvoker.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvoker.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvoker.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvoker.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvokerImpl.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvokerImpl.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvokerImpl.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvokerImpl.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniInvokerProvider.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniInvokerProvider.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniInvokerProvider.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniInvokerProvider.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniProvider.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniProvider.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniProvider.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniProvider.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvoker.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvoker.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvoker.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvoker.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvokerImpl.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvokerImpl.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvokerImpl.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/UniRxInvokerImpl.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/MultiProviderTest.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/MultiProviderTest.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/MultiProviderTest.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/MultiProviderTest.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniProviderTest.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniProviderTest.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniProviderTest.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniProviderTest.java diff --git a/extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniRxInvokerImplTest.java b/extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniRxInvokerImplTest.java similarity index 100% rename from extensions/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniRxInvokerImplTest.java rename to extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/test/java/io/quarkus/resteasy/mutiny/common/test/UniRxInvokerImplTest.java diff --git a/extensions/resteasy-mutiny/deployment/pom.xml b/extensions/resteasy-classic/resteasy-mutiny/deployment/pom.xml similarity index 100% rename from extensions/resteasy-mutiny/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny/deployment/pom.xml diff --git a/extensions/resteasy-mutiny/deployment/src/main/java/io/quarkus/resteasy/mutiny/deployment/ResteasyMutinyProcessor.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/main/java/io/quarkus/resteasy/mutiny/deployment/ResteasyMutinyProcessor.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/main/java/io/quarkus/resteasy/mutiny/deployment/ResteasyMutinyProcessor.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/main/java/io/quarkus/resteasy/mutiny/deployment/ResteasyMutinyProcessor.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyInjector.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyInjector.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyInjector.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyInjector.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyResource.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyResource.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyResource.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/MutinyResource.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/RestEasyMutinyTest.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/RestEasyMutinyTest.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/RestEasyMutinyTest.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/RestEasyMutinyTest.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/annotations/Async.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/annotations/Async.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/annotations/Async.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/annotations/Async.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/ResourceProducingJsonObject.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/ResourceProducingJsonObject.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/ResourceProducingJsonObject.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/ResourceProducingJsonObject.java diff --git a/extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/RestEasyMutinyWithJsonTest.java b/extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/RestEasyMutinyWithJsonTest.java similarity index 100% rename from extensions/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/RestEasyMutinyWithJsonTest.java rename to extensions/resteasy-classic/resteasy-mutiny/deployment/src/test/java/io/quarkus/resteasy/mutiny/test/vertx/RestEasyMutinyWithJsonTest.java diff --git a/extensions/resteasy-mutiny/pom.xml b/extensions/resteasy-classic/resteasy-mutiny/pom.xml similarity index 90% rename from extensions/resteasy-mutiny/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny/pom.xml index dd53e29fe5ea42..fcd70b3a9422bc 100644 --- a/extensions/resteasy-mutiny/pom.xml +++ b/extensions/resteasy-classic/resteasy-mutiny/pom.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-mutiny/runtime/pom.xml b/extensions/resteasy-classic/resteasy-mutiny/runtime/pom.xml similarity index 100% rename from extensions/resteasy-mutiny/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-mutiny/runtime/pom.xml diff --git a/extensions/resteasy-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-qute/deployment/pom.xml b/extensions/resteasy-classic/resteasy-qute/deployment/pom.xml similarity index 100% rename from extensions/resteasy-qute/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-qute/deployment/pom.xml diff --git a/extensions/resteasy-qute/deployment/src/main/java/io/quarkus/resteasy/qute/deployment/ResteasyQuteProcessor.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/main/java/io/quarkus/resteasy/qute/deployment/ResteasyQuteProcessor.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/main/java/io/quarkus/resteasy/qute/deployment/ResteasyQuteProcessor.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/main/java/io/quarkus/resteasy/qute/deployment/ResteasyQuteProcessor.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessageHelloResource.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessageHelloResource.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessageHelloResource.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessageHelloResource.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessages.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessages.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessages.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/AppMessages.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/HelloResource.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/HelloResource.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/HelloResource.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/HelloResource.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/ItemResource.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/ItemResource.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/ItemResource.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/ItemResource.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleDevModeTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleDevModeTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleDevModeTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleDevModeTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleLocaleFromVariantTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleLocaleFromVariantTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleLocaleFromVariantTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MessageBundleLocaleFromVariantTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateResource.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateResource.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateResource.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateResource.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TemplateResponseFilterTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TemplateResponseFilterTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TemplateResponseFilterTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TemplateResponseFilterTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/Templates.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/Templates.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/Templates.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/Templates.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorResource.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorResource.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorResource.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorResource.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest3.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest3.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest3.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/TypeErrorTest3.java diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/VariantTemplateTest.java b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/VariantTemplateTest.java similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/VariantTemplateTest.java rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/VariantTemplateTest.java diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/hello.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/hello.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/hello.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/hello.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError2.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError2.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError2.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typeError2.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.html b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.html similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.html rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.html diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplate.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplatePrimitives.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplatePrimitives.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplatePrimitives.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/HelloResource/typedTemplatePrimitives.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/MissingTemplateResource/hello.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/MissingTemplateResource/hello.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/MissingTemplateResource/hello.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/MissingTemplateResource/hello.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/TypeErrorResource/typeError3.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/TypeErrorResource/typeError3.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/TypeErrorResource/typeError3.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/TypeErrorResource/typeError3.txt diff --git a/extensions/resteasy-qute/deployment/src/test/resources/templates/toplevel.txt b/extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/toplevel.txt similarity index 100% rename from extensions/resteasy-qute/deployment/src/test/resources/templates/toplevel.txt rename to extensions/resteasy-classic/resteasy-qute/deployment/src/test/resources/templates/toplevel.txt diff --git a/extensions/resteasy-qute/pom.xml b/extensions/resteasy-classic/resteasy-qute/pom.xml similarity index 90% rename from extensions/resteasy-qute/pom.xml rename to extensions/resteasy-classic/resteasy-qute/pom.xml index 3f14d943dae217..4bc80fa22a7267 100644 --- a/extensions/resteasy-qute/pom.xml +++ b/extensions/resteasy-classic/resteasy-qute/pom.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-qute/runtime/pom.xml b/extensions/resteasy-classic/resteasy-qute/runtime/pom.xml similarity index 100% rename from extensions/resteasy-qute/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-qute/runtime/pom.xml diff --git a/extensions/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/RestTemplate.java b/extensions/resteasy-classic/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/RestTemplate.java similarity index 100% rename from extensions/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/RestTemplate.java rename to extensions/resteasy-classic/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/RestTemplate.java diff --git a/extensions/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/runtime/TemplateResponseFilter.java b/extensions/resteasy-classic/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/runtime/TemplateResponseFilter.java similarity index 100% rename from extensions/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/runtime/TemplateResponseFilter.java rename to extensions/resteasy-classic/resteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/runtime/TemplateResponseFilter.java diff --git a/extensions/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 81% rename from extensions/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 52c45b00b27920..f6cf411f8601ec 100644 --- a/extensions/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/resteasy-classic/resteasy-qute/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -15,4 +15,4 @@ metadata: languages: - "java" - "kotlin" - artifact: "io.quarkus:quarkus-descriptor-json" \ No newline at end of file + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" \ No newline at end of file diff --git a/extensions/resteasy-server-common/deployment/pom.xml b/extensions/resteasy-classic/resteasy-server-common/deployment/pom.xml similarity index 100% rename from extensions/resteasy-server-common/deployment/pom.xml rename to extensions/resteasy-classic/resteasy-server-common/deployment/pom.xml diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentCustomizerBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentCustomizerBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentCustomizerBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentCustomizerBuildItem.java diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyLogFilterBuildStep.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyLogFilterBuildStep.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyLogFilterBuildStep.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyLogFilterBuildStep.java diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java diff --git a/extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServletMappingBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServletMappingBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServletMappingBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServletMappingBuildItem.java diff --git a/extensions/resteasy-server-common/pom.xml b/extensions/resteasy-classic/resteasy-server-common/pom.xml similarity index 91% rename from extensions/resteasy-server-common/pom.xml rename to extensions/resteasy-classic/resteasy-server-common/pom.xml index ad6f30dfae832b..8c5c71540d7a41 100644 --- a/extensions/resteasy-server-common/pom.xml +++ b/extensions/resteasy-classic/resteasy-server-common/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy-server-common/runtime/pom.xml b/extensions/resteasy-classic/resteasy-server-common/runtime/pom.xml similarity index 100% rename from extensions/resteasy-server-common/runtime/pom.xml rename to extensions/resteasy-classic/resteasy-server-common/runtime/pom.xml diff --git a/extensions/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/QuarkusResteasyDeployment.java b/extensions/resteasy-classic/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/QuarkusResteasyDeployment.java similarity index 100% rename from extensions/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/QuarkusResteasyDeployment.java rename to extensions/resteasy-classic/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/QuarkusResteasyDeployment.java diff --git a/extensions/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/RESTEasyTestHttpProvider.java b/extensions/resteasy-classic/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/RESTEasyTestHttpProvider.java similarity index 100% rename from extensions/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/RESTEasyTestHttpProvider.java rename to extensions/resteasy-classic/resteasy-server-common/runtime/src/main/java/io/quarkus/resteasy/server/common/runtime/RESTEasyTestHttpProvider.java diff --git a/extensions/resteasy-server-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy-server-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 100% rename from extensions/resteasy-server-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy-server-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/resteasy-server-common/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.test.TestHttpEndpointProvider b/extensions/resteasy-classic/resteasy-server-common/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.test.TestHttpEndpointProvider similarity index 100% rename from extensions/resteasy-server-common/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.test.TestHttpEndpointProvider rename to extensions/resteasy-classic/resteasy-server-common/runtime/src/main/resources/META-INF/services/io.quarkus.runtime.test.TestHttpEndpointProvider diff --git a/extensions/resteasy-server-common/spi/pom.xml b/extensions/resteasy-classic/resteasy-server-common/spi/pom.xml similarity index 100% rename from extensions/resteasy-server-common/spi/pom.xml rename to extensions/resteasy-classic/resteasy-server-common/spi/pom.xml diff --git a/extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceDefiningAnnotationBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceDefiningAnnotationBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceDefiningAnnotationBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceDefiningAnnotationBuildItem.java diff --git a/extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodAnnotationsBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodAnnotationsBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodAnnotationsBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodAnnotationsBuildItem.java diff --git a/extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodParamAnnotations.java b/extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodParamAnnotations.java similarity index 100% rename from extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodParamAnnotations.java rename to extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AdditionalJaxRsResourceMethodParamAnnotations.java diff --git a/extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AllowedJaxRsAnnotationPrefixBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AllowedJaxRsAnnotationPrefixBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AllowedJaxRsAnnotationPrefixBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/AllowedJaxRsAnnotationPrefixBuildItem.java diff --git a/extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/ResteasyJaxrsConfigBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/ResteasyJaxrsConfigBuildItem.java similarity index 100% rename from extensions/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/ResteasyJaxrsConfigBuildItem.java rename to extensions/resteasy-classic/resteasy-server-common/spi/src/main/java/io/quarkus/resteasy/server/common/spi/ResteasyJaxrsConfigBuildItem.java diff --git a/extensions/resteasy/deployment/pom.xml b/extensions/resteasy-classic/resteasy/deployment/pom.xml similarity index 100% rename from extensions/resteasy/deployment/pom.xml rename to extensions/resteasy-classic/resteasy/deployment/pom.xml diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/DenyJaxRsTransformer.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/DenyJaxRsTransformer.java similarity index 100% rename from extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/DenyJaxRsTransformer.java rename to extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/DenyJaxRsTransformer.java diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyBuiltinsProcessor.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyBuiltinsProcessor.java similarity index 100% rename from extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyBuiltinsProcessor.java rename to extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyBuiltinsProcessor.java diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyJaxrsConfigBuildItem.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyJaxrsConfigBuildItem.java similarity index 100% rename from extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyJaxrsConfigBuildItem.java rename to extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyJaxrsConfigBuildItem.java diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java similarity index 100% rename from extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java rename to extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java similarity index 100% rename from extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java rename to extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheControlFeatureTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheControlFeatureTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheControlFeatureTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheControlFeatureTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CacheResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ClasspathResourceTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ClasspathResourceTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ClasspathResourceTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ClasspathResourceTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResourceTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResourceTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResourceTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ConstructorInjectionResourceTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContentLengthTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContextTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContextTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContextTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ContextTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CookieTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CookieTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CookieTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CookieTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CustomExceptionMapperTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CustomExceptionMapperTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CustomExceptionMapperTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/CustomExceptionMapperTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ExceptionReturnStatusAndBodyTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ExceptionReturnStatusAndBodyTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ExceptionReturnStatusAndBodyTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ExceptionReturnStatusAndBodyTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ForwardedEnabledTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ForwardedEnabledTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ForwardedEnabledTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ForwardedEnabledTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/GreetingResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/GreetingResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/GreetingResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/GreetingResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/IncompletePostTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/IncompletePostTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/IncompletePostTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/IncompletePostTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResourceImpl.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResourceImpl.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResourceImpl.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InterfaceResourceImpl.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonProviderTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonProviderTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonProviderTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonProviderTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/JsonResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxBodySizeResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxBodySizeResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxBodySizeResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxBodySizeResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxRequestSizeTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxRequestSizeTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxRequestSizeTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/MaxRequestSizeTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTrailingSlashTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTrailingSlashTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTrailingSlashTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperHttpRootTrailingSlashTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/NotFoundExceptionMapperTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PathInterfaceImplementorTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PathInterfaceImplementorTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PathInterfaceImplementorTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PathInterfaceImplementorTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostEndpoint.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostEndpoint.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostEndpoint.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostEndpoint.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/PostResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ProviderConfigInjectionTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ProviderConfigInjectionTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ProviderConfigInjectionTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ProviderConfigInjectionTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ReadTimeoutTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ReadTimeoutTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ReadTimeoutTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/ReadTimeoutTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyCORSTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyCORSTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyCORSTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyCORSTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyDevModeTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyDevModeTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyDevModeTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyDevModeTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyRootResourceTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyRootResourceTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyRootResourceTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RestEasyRootResourceTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RootResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RootResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RootResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/RootResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/Service.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/Service.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/Service.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/Service.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/SetCookieResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/SetCookieResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/SetCookieResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/SetCookieResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOUndertowTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOUndertowTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOUndertowTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/asyncio/AsyncIOUndertowTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/BeanGenerationTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/BeanGenerationTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/BeanGenerationTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/BeanGenerationTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/Greeting.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/Greeting.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/Greeting.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/Greeting.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/GreetingEndpoint.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/GreetingEndpoint.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/GreetingEndpoint.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/GreetingEndpoint.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/MorningGreeting.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/MorningGreeting.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/MorningGreeting.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/MorningGreeting.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/Hello.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/Hello.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/Hello.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/Hello.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/PublicHello.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/PublicHello.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/PublicHello.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/cdi/internal/PublicHello.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/NoResourceTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/NoResourceTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/NoResourceTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/NoResourceTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesHttpRootTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesHttpRootTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesHttpRootTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesHttpRootTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithResourcesTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithoutResourcesTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithoutResourcesTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithoutResourcesTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticFileWithoutResourcesTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticResourceDeletionBeforeFirstRequestTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticResourceDeletionBeforeFirstRequestTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticResourceDeletionBeforeFirstRequestTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/files/StaticResourceDeletionBeforeFirstRequestTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResourceTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResourceTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResourceTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/gzip/GZipResourceTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResourceTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResourceTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResourceTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/multipart/MultipartResourceTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/BuildProfileTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/BuildProfileTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/BuildProfileTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/BuildProfileTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/route/UserRouteTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/route/UserRouteTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/route/UserRouteTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/route/UserRouteTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/DenyAllJaxRsTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/DenyAllJaxRsTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/DenyAllJaxRsTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/DenyAllJaxRsTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/LazyAuthRolesAllowedJaxRsTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/LazyAuthRolesAllowedJaxRsTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/LazyAuthRolesAllowedJaxRsTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/LazyAuthRolesAllowedJaxRsTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/PermitAllResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/PermitAllResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/PermitAllResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/PermitAllResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedJaxRsTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedJaxRsTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedJaxRsTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedJaxRsTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityOverrideFilter.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityOverrideFilter.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityOverrideFilter.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityOverrideFilter.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredSubResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredSubResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredSubResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UnsecuredSubResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UserResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UserResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UserResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/UserResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseTestCase.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseTestCase.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseTestCase.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/sse/SseTestCase.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/MyService.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/MyService.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/MyService.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/MyService.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingsResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingsResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingsResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/PingsResource.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorHttpRootTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorHttpRootTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorHttpRootTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorHttpRootTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorTest.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorTest.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SubresourceLocatorTest.java diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SuperPingResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SuperPingResource.java similarity index 100% rename from extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SuperPingResource.java rename to extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/subresource/SuperPingResource.java diff --git a/extensions/resteasy/deployment/src/test/resources/application-asyncio.properties b/extensions/resteasy-classic/resteasy/deployment/src/test/resources/application-asyncio.properties similarity index 100% rename from extensions/resteasy/deployment/src/test/resources/application-asyncio.properties rename to extensions/resteasy-classic/resteasy/deployment/src/test/resources/application-asyncio.properties diff --git a/extensions/resteasy/deployment/src/test/resources/config-test.properties b/extensions/resteasy-classic/resteasy/deployment/src/test/resources/config-test.properties similarity index 100% rename from extensions/resteasy/deployment/src/test/resources/config-test.properties rename to extensions/resteasy-classic/resteasy/deployment/src/test/resources/config-test.properties diff --git a/extensions/resteasy/deployment/src/test/resources/cors-config.properties b/extensions/resteasy-classic/resteasy/deployment/src/test/resources/cors-config.properties similarity index 100% rename from extensions/resteasy/deployment/src/test/resources/cors-config.properties rename to extensions/resteasy-classic/resteasy/deployment/src/test/resources/cors-config.properties diff --git a/extensions/resteasy/deployment/src/test/resources/index.html b/extensions/resteasy-classic/resteasy/deployment/src/test/resources/index.html similarity index 100% rename from extensions/resteasy/deployment/src/test/resources/index.html rename to extensions/resteasy-classic/resteasy/deployment/src/test/resources/index.html diff --git a/extensions/resteasy/deployment/src/test/resources/lorem.txt b/extensions/resteasy-classic/resteasy/deployment/src/test/resources/lorem.txt similarity index 100% rename from extensions/resteasy/deployment/src/test/resources/lorem.txt rename to extensions/resteasy-classic/resteasy/deployment/src/test/resources/lorem.txt diff --git a/extensions/resteasy/pom.xml b/extensions/resteasy-classic/resteasy/pom.xml similarity index 90% rename from extensions/resteasy/pom.xml rename to extensions/resteasy-classic/resteasy/pom.xml index 7e795c79447737..4a63eea2618491 100644 --- a/extensions/resteasy/pom.xml +++ b/extensions/resteasy-classic/resteasy/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - quarkus-extensions-parent + quarkus-resteasy-parent-aggregator io.quarkus 999-SNAPSHOT ../pom.xml diff --git a/extensions/resteasy/runtime/pom.xml b/extensions/resteasy-classic/resteasy/runtime/pom.xml similarity index 100% rename from extensions/resteasy/runtime/pom.xml rename to extensions/resteasy-classic/resteasy/runtime/pom.xml diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationCompletionExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationCompletionExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationCompletionExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationCompletionExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationFailedExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationFailedExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationFailedExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationFailedExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationRedirectExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationRedirectExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationRedirectExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/AuthenticationRedirectExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/CompositeExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/CompositeExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/CompositeExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/CompositeExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ContextUtil.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ContextUtil.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ContextUtil.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ContextUtil.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ExceptionMapperRecorder.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ExceptionMapperRecorder.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ExceptionMapperRecorder.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ExceptionMapperRecorder.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ForbiddenExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ForbiddenExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ForbiddenExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ForbiddenExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NonJaxRsClassMappings.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NonJaxRsClassMappings.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NonJaxRsClassMappings.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NonJaxRsClassMappings.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NotFoundExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NotFoundExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NotFoundExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/NotFoundExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyFilter.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyFilter.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyFilter.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyFilter.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyServlet.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyServlet.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyServlet.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyServlet.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyVertxConfig.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyVertxConfig.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyVertxConfig.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ResteasyVertxConfig.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/UnauthorizedExceptionMapper.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/UnauthorizedExceptionMapper.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/UnauthorizedExceptionMapper.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/UnauthorizedExceptionMapper.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/BufferAllocator.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/BufferAllocator.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/BufferAllocator.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/BufferAllocator.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/LazyHostSupplier.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/LazyHostSupplier.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/LazyHostSupplier.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/LazyHostSupplier.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/QuarkusResteasySecurityContext.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/QuarkusResteasySecurityContext.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/QuarkusResteasySecurityContext.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/QuarkusResteasySecurityContext.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/RequestDispatcher.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/RequestDispatcher.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/RequestDispatcher.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/RequestDispatcher.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyConfigurationMPConfig.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyConfigurationMPConfig.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyConfigurationMPConfig.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyConfigurationMPConfig.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxBlockingOutput.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxBlockingOutput.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxBlockingOutput.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxBlockingOutput.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpResponse.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpResponse.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpResponse.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpResponse.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutput.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutput.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutput.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutput.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutputStream.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutputStream.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutputStream.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxOutputStream.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxUtil.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxUtil.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxUtil.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxUtil.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayReader.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayReader.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayReader.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayReader.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayWriter.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayWriter.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayWriter.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonArrayWriter.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectReader.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectReader.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectReader.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectReader.java diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectWriter.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectWriter.java similarity index 100% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectWriter.java rename to extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/vertx/JsonObjectWriter.java diff --git a/extensions/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-classic/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 82% rename from extensions/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/resteasy-classic/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 19c44114cb2ea9..5e00f4a8b8e4e3 100644 --- a/extensions/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/resteasy-classic/resteasy/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -17,4 +17,4 @@ metadata: - "java" - "kotlin" - "scala" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/resteasy-reactive/pom.xml b/extensions/resteasy-reactive/pom.xml index ce1ac2e2602b1e..e7db538b82c123 100644 --- a/extensions/resteasy-reactive/pom.xml +++ b/extensions/resteasy-reactive/pom.xml @@ -18,10 +18,13 @@ quarkus-resteasy-reactive quarkus-jaxrs-client quarkus-resteasy-reactive-jackson + quarkus-resteasy-reactive-jackson-common quarkus-resteasy-reactive-jsonb quarkus-resteasy-reactive-qute quarkus-resteasy-reactive-servlet rest-client-reactive + rest-client-reactive-jackson + quarkus-resteasy-reactive-links diff --git a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java index 46a7d1c41954d1..769d1932fad811 100644 --- a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java +++ b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java @@ -96,7 +96,9 @@ import io.quarkus.resteasy.reactive.common.deployment.SerializersUtil; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveConfig; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem; import io.quarkus.runtime.RuntimeValue; public class JaxrsClientProcessor { @@ -132,6 +134,8 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, BuildProducer reflectiveClassBuildItemBuildProducer, List messageBodyReaderBuildItems, List messageBodyWriterBuildItems, + List messageBodyReaderOverrideBuildItems, + List messageBodyWriterOverrideBuildItems, List enricherBuildItems, BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, Optional resourceScanningResultBuildItem, @@ -147,7 +151,8 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, Serialisers serialisers = recorder.createSerializers(); SerializersUtil.setupSerializers(recorder, reflectiveClassBuildItemBuildProducer, messageBodyReaderBuildItems, - messageBodyWriterBuildItems, beanContainerBuildItem, applicationResultBuildItem, serialisers, + messageBodyWriterBuildItems, messageBodyReaderOverrideBuildItems, messageBodyWriterOverrideBuildItems, + beanContainerBuildItem, applicationResultBuildItem, serialisers, RuntimeType.CLIENT); if (!resourceScanningResultBuildItem.isPresent() diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java index 7b5716bac90d37..38a8dc8b417c1c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java @@ -10,6 +10,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import javax.ws.rs.Priorities; import javax.ws.rs.RuntimeType; import javax.ws.rs.ext.RuntimeDelegate; @@ -48,7 +49,9 @@ import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.ReaderInterceptorBuildItem; import io.quarkus.resteasy.reactive.spi.WriterInterceptorBuildItem; import io.quarkus.security.spi.AdditionalSecuredClassesBuildIem; @@ -56,6 +59,9 @@ public class ResteasyReactiveCommonProcessor { + private static final int LEGACY_READER_PRIORITY = Priorities.USER * 2; // readers are compared by decreased priority + private static final int LEGACY_WRITER_PRIORITY = Priorities.USER / 2; // writers are compared by increased priority + @BuildStep void setUpDenyAllJaxRs(CombinedIndexBuildItem index, JaxRsSecurityConfig config, @@ -151,6 +157,9 @@ protected void registerInterceptors( if (priority != null) { interceptor.setPriority(priority); } + if (filterItem instanceof ContainerRequestFilterBuildItem) { + interceptor.setNonBlockingRequired(((ContainerRequestFilterBuildItem) filterItem).isNonBlockingRequired()); + } if (interceptors instanceof PreMatchInterceptorContainer && ((ContainerRequestFilterBuildItem) filterItem).isPreMatching()) { ((PreMatchInterceptorContainer) interceptors).addPreMatchInterceptor(interceptor); @@ -249,8 +258,13 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, if (constrainedToInstance != null) { runtimeType = RuntimeType.valueOf(constrainedToInstance.value().asEnum()); } + int priority = Priorities.USER; + AnnotationInstance priorityInstance = writerClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + if (priorityInstance != null) { + priority = priorityInstance.value().asInt(); + } messageBodyWriterBuildItemBuildProducer.produce(new MessageBodyWriterBuildItem(writerClassName, - typeParameters.get(0).name().toString(), mediaTypeStrings, runtimeType, false)); + typeParameters.get(0).name().toString(), mediaTypeStrings, runtimeType, false, priority)); } } @@ -274,8 +288,13 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, if (constrainedToInstance != null) { runtimeType = RuntimeType.valueOf(constrainedToInstance.value().asEnum()); } + int priority = Priorities.USER; + AnnotationInstance priorityInstance = readerClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + if (priorityInstance != null) { + priority = priorityInstance.value().asInt(); + } messageBodyReaderBuildItemBuildProducer.produce(new MessageBodyReaderBuildItem(readerClassName, - typeParameters.get(0).name().toString(), mediaTypeStrings, runtimeType, false)); + typeParameters.get(0).name().toString(), mediaTypeStrings, runtimeType, false, priority)); reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, readerClassName)); } } @@ -287,6 +306,31 @@ void registerRuntimeDelegateImpl(BuildProducer service RuntimeDelegateImpl.class.getName())); } + /* + * There are some MessageBodyReaders and MessageBodyWriters that are brought in transitively + * by the inclusion of the extension like the 'quarkus-keycloak-admin-client'. + * We need to make sure that these providers are not selected over the ones that our Quarkus extensions provide. + * To do that, we first need to make them built-in (as the spec mandates that non-build-in providers are choosen + * over built-in ones) and then we also need to change their priority + */ + @BuildStep + void deprioritizeLegacyProviders(BuildProducer readers, + BuildProducer writers) { + readers.produce(new MessageBodyReaderOverrideBuildItem( + "org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider", LEGACY_READER_PRIORITY, true)); + readers.produce(new MessageBodyReaderOverrideBuildItem("com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider", + LEGACY_READER_PRIORITY, true)); + readers.produce(new MessageBodyReaderOverrideBuildItem("com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider", + LEGACY_READER_PRIORITY, true)); + + writers.produce(new MessageBodyWriterOverrideBuildItem( + "org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider", LEGACY_WRITER_PRIORITY, true)); + writers.produce(new MessageBodyWriterOverrideBuildItem("com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider", + LEGACY_WRITER_PRIORITY, true)); + writers.produce(new MessageBodyWriterOverrideBuildItem("com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider", + LEGACY_WRITER_PRIORITY, true)); + } + /** * @param buildTimeConditions the build time conditions from which the excluded classes are extracted. * @return the set of classes that have been annotated with unsuccessful build time conditions. diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java index ab449cab2be3e6..ed7b85ae91ad02 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java @@ -1,6 +1,8 @@ package io.quarkus.resteasy.reactive.common.deployment; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.ws.rs.RuntimeType; @@ -13,7 +15,10 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveCommonRecorder; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderWriterOverrideData; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.RuntimeTypeItem; public class SerializersUtil { @@ -22,15 +27,26 @@ public static void setupSerializers(ResteasyReactiveCommonRecorder recorder, BuildProducer reflectiveClass, List messageBodyReaderBuildItems, List messageBodyWriterBuildItems, + List messageBodyReaderOverrideBuildItems, + List messageBodyWriterOverrideBuildItems, BeanContainerBuildItem beanContainerBuildItem, ApplicationResultBuildItem applicationResultBuildItem, Serialisers serialisers, RuntimeType runtimeType) { + Map writerOverrides = new HashMap<>(); + for (MessageBodyWriterOverrideBuildItem writerOverride : messageBodyWriterOverrideBuildItems) { + writerOverrides.put(writerOverride.getClassName(), writerOverride.getOverrideData()); + } for (MessageBodyWriterBuildItem additionalWriter : RuntimeTypeItem.filter(messageBodyWriterBuildItems, runtimeType)) { ResourceWriter writer = new ResourceWriter(); - writer.setBuiltin(additionalWriter.isBuiltin()); String writerClassName = additionalWriter.getClassName(); + MessageBodyReaderWriterOverrideData overrideData = writerOverrides.get(writerClassName); + if (overrideData != null) { + writer.setBuiltin(overrideData.isBuiltIn()); + } else { + writer.setBuiltin(additionalWriter.isBuiltin()); + } writer.setFactory(FactoryUtils.factory(writerClassName, applicationResultBuildItem.getResult().getSingletonClasses(), recorder, beanContainerBuildItem)); @@ -38,14 +54,29 @@ public static void setupSerializers(ResteasyReactiveCommonRecorder recorder, if (!additionalWriter.getMediaTypeStrings().isEmpty()) { writer.setMediaTypeStrings(additionalWriter.getMediaTypeStrings()); } + if (overrideData != null) { + writer.setPriority(overrideData.getPriority()); + } else { + writer.setPriority(additionalWriter.getPriority()); + } recorder.registerWriter(serialisers, additionalWriter.getHandledClassName(), writer); reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, writerClassName)); } + + Map readerOverrides = new HashMap<>(); + for (MessageBodyReaderOverrideBuildItem readerOverride : messageBodyReaderOverrideBuildItems) { + readerOverrides.put(readerOverride.getClassName(), readerOverride.getOverrideData()); + } for (MessageBodyReaderBuildItem additionalReader : RuntimeTypeItem.filter(messageBodyReaderBuildItems, runtimeType)) { ResourceReader reader = new ResourceReader(); - reader.setBuiltin(additionalReader.isBuiltin()); String readerClassName = additionalReader.getClassName(); + MessageBodyReaderWriterOverrideData overrideData = readerOverrides.get(readerClassName); + if (overrideData != null) { + reader.setBuiltin(overrideData.isBuiltIn()); + } else { + reader.setBuiltin(additionalReader.isBuiltin()); + } reader.setFactory(FactoryUtils.factory(readerClassName, applicationResultBuildItem.getResult().getSingletonClasses(), recorder, beanContainerBuildItem)); @@ -53,6 +84,11 @@ public static void setupSerializers(ResteasyReactiveCommonRecorder recorder, if (!additionalReader.getMediaTypeStrings().isEmpty()) { reader.setMediaTypeStrings(additionalReader.getMediaTypeStrings()); } + if (overrideData != null) { + reader.setPriority(overrideData.getPriority()); + } else { + reader.setPriority(additionalReader.getPriority()); + } recorder.registerReader(serialisers, additionalReader.getHandledClassName(), reader); reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, readerClassName)); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/ContainerRequestFilterBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/ContainerRequestFilterBuildItem.java index 356291171e1861..a8c0b40cf72b5a 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/ContainerRequestFilterBuildItem.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/ContainerRequestFilterBuildItem.java @@ -3,23 +3,31 @@ public final class ContainerRequestFilterBuildItem extends AbstractInterceptorBuildItem { private final boolean preMatching; + private final boolean nonBlockingRequired; protected ContainerRequestFilterBuildItem(Builder builder) { super(builder); this.preMatching = builder.preMatching; + this.nonBlockingRequired = builder.nonBlockingRequired; } public ContainerRequestFilterBuildItem(String className) { super(className); this.preMatching = false; + this.nonBlockingRequired = false; } public boolean isPreMatching() { return preMatching; } + public boolean isNonBlockingRequired() { + return nonBlockingRequired; + } + public static final class Builder extends AbstractInterceptorBuildItem.Builder { boolean preMatching = false; + boolean nonBlockingRequired = false; public Builder(String className) { super(className); @@ -30,6 +38,11 @@ public Builder setPreMatching(Boolean preMatching) { return this; } + public Builder setNonBlockingRequired(Boolean nonBlockingRequired) { + this.nonBlockingRequired = nonBlockingRequired; + return this; + } + public ContainerRequestFilterBuildItem build() { return new ContainerRequestFilterBuildItem(this); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderBuildItem.java index 2c887e7adc5922..adb85c0b29d4a9 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderBuildItem.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderBuildItem.java @@ -2,6 +2,7 @@ import java.util.List; +import javax.ws.rs.Priorities; import javax.ws.rs.RuntimeType; import io.quarkus.builder.item.MultiBuildItem; @@ -13,18 +14,20 @@ public final class MessageBodyReaderBuildItem extends MultiBuildItem implements private final List mediaTypeStrings; private final RuntimeType runtimeType; private final boolean builtin; + private final Integer priority; public MessageBodyReaderBuildItem(String className, String handledClassName, List mediaTypeStrings) { - this(className, handledClassName, mediaTypeStrings, null, true); + this(className, handledClassName, mediaTypeStrings, null, true, Priorities.USER); } public MessageBodyReaderBuildItem(String className, String handledClassName, List mediaTypeStrings, - RuntimeType runtimeType, boolean builtin) { + RuntimeType runtimeType, boolean builtin, Integer priority) { this.className = className; this.handledClassName = handledClassName; this.mediaTypeStrings = mediaTypeStrings; this.runtimeType = runtimeType; this.builtin = builtin; + this.priority = priority; } public String getClassName() { @@ -47,4 +50,8 @@ public RuntimeType getRuntimeType() { public boolean isBuiltin() { return builtin; } + + public Integer getPriority() { + return priority; + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderOverrideBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderOverrideBuildItem.java new file mode 100644 index 00000000000000..9ec7b11ec08a1c --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderOverrideBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.spi; + +import io.quarkus.builder.item.MultiBuildItem; + +public final class MessageBodyReaderOverrideBuildItem extends MultiBuildItem { + + private final String className; + private final MessageBodyReaderWriterOverrideData overrideData; + + public MessageBodyReaderOverrideBuildItem(String className, int priority, boolean builtIn) { + this.className = className; + this.overrideData = new MessageBodyReaderWriterOverrideData(priority, builtIn); + + } + + public String getClassName() { + return className; + } + + public MessageBodyReaderWriterOverrideData getOverrideData() { + return overrideData; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderWriterOverrideData.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderWriterOverrideData.java new file mode 100644 index 00000000000000..593024f3a729eb --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyReaderWriterOverrideData.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.spi; + +public final class MessageBodyReaderWriterOverrideData { + + private final int priority; + private final boolean builtIn; + + public MessageBodyReaderWriterOverrideData(int priority, boolean builtIn) { + this.priority = priority; + this.builtIn = builtIn; + } + + public int getPriority() { + return priority; + } + + public boolean isBuiltIn() { + return builtIn; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterBuildItem.java index 75c638501b3c81..9b117265227400 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterBuildItem.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterBuildItem.java @@ -2,6 +2,7 @@ import java.util.List; +import javax.ws.rs.Priorities; import javax.ws.rs.RuntimeType; import io.quarkus.builder.item.MultiBuildItem; @@ -13,18 +14,20 @@ public final class MessageBodyWriterBuildItem extends MultiBuildItem implements private final List mediaTypeStrings; private final RuntimeType runtimeType; private final boolean builtin; + private final Integer priority; public MessageBodyWriterBuildItem(String className, String handledClassName, List mediaTypeStrings) { - this(className, handledClassName, mediaTypeStrings, null, false); + this(className, handledClassName, mediaTypeStrings, null, false, Priorities.USER); } public MessageBodyWriterBuildItem(String className, String handledClassName, List mediaTypeStrings, - RuntimeType runtimeType, boolean builtin) { + RuntimeType runtimeType, boolean builtin, Integer priority) { this.className = className; this.handledClassName = handledClassName; this.mediaTypeStrings = mediaTypeStrings; this.runtimeType = runtimeType; this.builtin = builtin; + this.priority = priority; } public String getClassName() { @@ -47,4 +50,8 @@ public RuntimeType getRuntimeType() { public boolean isBuiltin() { return builtin; } + + public Integer getPriority() { + return priority; + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterOverrideBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterOverrideBuildItem.java new file mode 100644 index 00000000000000..b4d5a2e00bb7a3 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/MessageBodyWriterOverrideBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.spi; + +import io.quarkus.builder.item.MultiBuildItem; + +public final class MessageBodyWriterOverrideBuildItem extends MultiBuildItem { + + private final String className; + private final MessageBodyReaderWriterOverrideData overrideData; + + public MessageBodyWriterOverrideBuildItem(String className, int priority, boolean builtIn) { + this.className = className; + this.overrideData = new MessageBodyReaderWriterOverrideData(priority, builtIn); + + } + + public String getClassName() { + return className; + } + + public MessageBodyReaderWriterOverrideData getOverrideData() { + return overrideData; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/pom.xml new file mode 100644 index 00000000000000..54558edf2e2b51 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + quarkus-resteasy-reactive-jackson-common-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-resteasy-reactive-jackson-common-deployment + Quarkus - RESTEasy Reactive - Common Jackson Bits - Deployment + + + + io.quarkus + quarkus-resteasy-reactive-common-deployment + + + io.quarkus + quarkus-jackson-deployment + + + io.quarkus + quarkus-resteasy-reactive-jackson-common + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveCommonJacksonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveCommonJacksonProcessor.java new file mode 100644 index 00000000000000..d50985490da050 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveCommonJacksonProcessor.java @@ -0,0 +1,68 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.processor; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Type; + +import com.fasterxml.jackson.annotation.JsonView; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem; +import io.quarkus.resteasy.reactive.jackson.CustomSerialization; + +public class ResteasyReactiveCommonJacksonProcessor { + + private static final DotName JSON_VIEW = DotName.createSimple(JsonView.class.getName()); + private static final DotName CUSTOM_SERIALIZATION = DotName.createSimple(CustomSerialization.class.getName()); + + @BuildStep + void handleJsonAnnotations(Optional resourceScanningResultBuildItem, + CombinedIndexBuildItem index, + BuildProducer reflectiveClass) { + if (!resourceScanningResultBuildItem.isPresent()) { + return; + } + Collection resourceClasses = resourceScanningResultBuildItem.get().getResult().getScannedResources() + .values(); + Set classesNeedingReflectionOnMethods = new HashSet<>(); + for (ClassInfo resourceClass : resourceClasses) { + DotName resourceClassDotName = resourceClass.name(); + if (resourceClass.annotations().containsKey(JSON_VIEW)) { + classesNeedingReflectionOnMethods.add(resourceClassDotName.toString()); + } else if (resourceClass.annotations().containsKey(CUSTOM_SERIALIZATION)) { + classesNeedingReflectionOnMethods.add(resourceClassDotName.toString()); + for (AnnotationInstance instance : resourceClass.annotations().get(CUSTOM_SERIALIZATION)) { + AnnotationValue annotationValue = instance.value(); + if (annotationValue != null) { + Type biFunctionType = annotationValue.asClass(); + ClassInfo biFunctionClassInfo = index.getIndex().getClassByName(biFunctionType.name()); + if (biFunctionClassInfo == null) { + // be lenient + } else { + if (!biFunctionClassInfo.hasNoArgsConstructor()) { + throw new RuntimeException( + "Class '" + biFunctionClassInfo.name() + "' must contain a no-args constructor"); + } + } + reflectiveClass.produce( + new ReflectiveClassBuildItem(true, false, false, biFunctionType.name().toString())); + } + } + } + } + if (!classesNeedingReflectionOnMethods.isEmpty()) { + reflectiveClass.produce( + new ReflectiveClassBuildItem(true, false, classesNeedingReflectionOnMethods.toArray(new String[0]))); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java new file mode 100644 index 00000000000000..23fbb2d32db0e9 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java @@ -0,0 +1,11 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.processor; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * A BuildItem to mark that the server side jackson provider is defined. + * If not "emitted" by any of the processors, the reactive rest client (if used) will add its own jackson provider + */ +public class ResteasyReactiveJacksonProviderDefinedBuildItem extends MultiBuildItem { + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/pom.xml new file mode 100644 index 00000000000000..86af49466a2cde --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-resteasy-reactive-parent-aggregator + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-resteasy-reactive-jackson-common-parent + Quarkus - RESTEasy Reactive - Jackson Common Bits + pom + + deployment + runtime + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/pom.xml new file mode 100644 index 00000000000000..f40d962c4afdc7 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + quarkus-resteasy-reactive-jackson-common-parent + io.quarkus + 999-SNAPSHOT + + quarkus-resteasy-reactive-jackson-common + Quarkus - RESTEasy Reactive - Jackson Common Bits - Runtime + Common classes for Jackson serialization support for RESTEasy Reactive + + + + io.quarkus + quarkus-resteasy-reactive-common + + + io.quarkus + quarkus-jackson + + + io.quarkus + quarkus-jsonp + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/CustomSerialization.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/CustomSerialization.java similarity index 100% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/CustomSerialization.java rename to extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/CustomSerialization.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyReader.java new file mode 100644 index 00000000000000..ebcb0d39cfb8e7 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyReader.java @@ -0,0 +1,47 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.inject.Inject; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.reactive.common.providers.serialisers.AbstractJsonMessageBodyReader; +import org.jboss.resteasy.reactive.common.util.EmptyInputStream; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; + +public class JacksonBasicMessageBodyReader extends AbstractJsonMessageBodyReader { + + protected final ObjectReader reader; + + @Inject + public JacksonBasicMessageBodyReader(ObjectMapper mapper) { + this.reader = mapper.reader(); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + try { + return doReadFrom(type, genericType, entityStream); + } catch (MismatchedInputException e) { + throw new WebApplicationException(Response.Status.BAD_REQUEST); + } + } + + private Object doReadFrom(Class type, Type genericType, InputStream entityStream) throws IOException { + if (entityStream instanceof EmptyInputStream) { + return null; + } + return reader.forType(reader.getTypeFactory().constructType(genericType != null ? genericType : type)) + .readValue(entityStream); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyWriter.java new file mode 100644 index 00000000000000..d3e98a15bcfcb8 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonBasicMessageBodyWriter.java @@ -0,0 +1,91 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers; + +import static org.jboss.resteasy.reactive.common.providers.serialisers.JsonMessageBodyWriterUtil.setContentTypeIfNecessary; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Inject; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; + +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator.Feature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +import io.quarkus.resteasy.reactive.jackson.CustomSerialization; + +public class JacksonBasicMessageBodyWriter implements MessageBodyWriter { + + private static final String JSON_VIEW_NAME = JsonView.class.getName(); + private static final String CUSTOM_SERIALIZATION = CustomSerialization.class.getName(); + + protected final ObjectMapper originalMapper; + protected final ObjectWriter defaultWriter; + protected final ConcurrentMap perMethodWriter = new ConcurrentHashMap<>(); + + @Inject + public JacksonBasicMessageBodyWriter(ObjectMapper mapper) { + this.originalMapper = mapper; + // we don't want the ObjectWriter to close the stream automatically, as we want to handle closing manually at the proper points + JsonFactory jsonFactory = mapper.getFactory(); + if (needsNewFactory(jsonFactory)) { + jsonFactory = jsonFactory.copy(); + setNecessaryJsonFactoryConfig(jsonFactory); + this.defaultWriter = mapper.writer().with(jsonFactory); + } else { + this.defaultWriter = mapper.writer(); + } + } + + private boolean needsNewFactory(JsonFactory jsonFactory) { + return jsonFactory.isEnabled(Feature.AUTO_CLOSE_TARGET) || jsonFactory.isEnabled(Feature.FLUSH_PASSED_TO_STREAM); + } + + protected static void setNecessaryJsonFactoryConfig(JsonFactory jsonFactory) { + jsonFactory.configure(Feature.AUTO_CLOSE_TARGET, false); + jsonFactory.configure(Feature.FLUSH_PASSED_TO_STREAM, false); + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return true; + } + + @Override + public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + setContentTypeIfNecessary(httpHeaders); + if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... + entityStream.write(((String) o).getBytes()); + } else { + if (annotations != null) { + for (Annotation annotation : annotations) { + if (JsonView.class.equals(annotation.annotationType())) { + if (handleJsonView(((JsonView) annotation), o, entityStream)) { + return; + } + } + } + } + entityStream.write(defaultWriter.writeValueAsBytes(o)); + } + } + + private boolean handleJsonView(JsonView jsonView, Object o, OutputStream stream) throws IOException { + if ((jsonView != null) && (jsonView.value().length > 0)) { + defaultWriter.withView(jsonView.value()[0]).writeValue(stream, o); + return true; + } + return false; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyReader.java new file mode 100644 index 00000000000000..5fa3e73e790d2b --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyReader.java @@ -0,0 +1,37 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; + +import org.jboss.resteasy.reactive.common.providers.serialisers.PrimitiveBodyHandler; + +import io.vertx.core.json.JsonArray; + +public class VertxJsonArrayBasicMessageBodyReader extends PrimitiveBodyHandler implements MessageBodyReader { + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isReadable(type); + } + + @Override + public JsonArray readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + return readFrom(entityStream); + } + + protected boolean isReadable(Class type) { + return JsonArray.class.isAssignableFrom(type); + } + + protected JsonArray readFrom(InputStream entityStream) throws IOException { + return new JsonArray(readFrom(entityStream, false)); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyWriter.java new file mode 100644 index 00000000000000..b8d32dea2dd7ae --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayBasicMessageBodyWriter.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; + +import io.vertx.core.json.JsonArray; + +public class VertxJsonArrayBasicMessageBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isWriteable(type); + } + + @Override + public void writeTo(JsonArray o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + entityStream.write(o.encode().getBytes(StandardCharsets.UTF_8)); + } + + protected boolean isWriteable(Class type) { + return JsonArray.class.isAssignableFrom(type); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyReader.java new file mode 100644 index 00000000000000..80edf90521481f --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyReader.java @@ -0,0 +1,37 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; + +import org.jboss.resteasy.reactive.common.providers.serialisers.PrimitiveBodyHandler; + +import io.vertx.core.json.JsonObject; + +public class VertxJsonObjectBasicMessageBodyReader extends PrimitiveBodyHandler implements MessageBodyReader { + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isReadable(type); + } + + protected boolean isReadable(Class type) { + return JsonObject.class.isAssignableFrom(type); + } + + @Override + public JsonObject readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + return readFrom(entityStream); + } + + protected JsonObject readFrom(InputStream entityStream) throws IOException { + return new JsonObject(readFrom(entityStream, false)); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyWriter.java new file mode 100644 index 00000000000000..7d79e758ce9eff --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectBasicMessageBodyWriter.java @@ -0,0 +1,33 @@ +package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; + +import io.vertx.core.json.JsonObject; + +public class VertxJsonObjectBasicMessageBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isWriteable(type); + } + + protected boolean isWriteable(Class type) { + return JsonObject.class.isAssignableFrom(type); + } + + @Override + public void writeTo(JsonObject o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + entityStream.write(o.encode().getBytes(StandardCharsets.UTF_8)); + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/pom.xml index cf1b5424ddc54f..d89c9b3b3e4c23 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/pom.xml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/pom.xml @@ -19,7 +19,7 @@ io.quarkus - quarkus-jackson-deployment + quarkus-resteasy-reactive-jackson-common-deployment io.quarkus diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java index cec2063beaa777..b31ce52234ca03 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -1,31 +1,15 @@ package io.quarkus.resteasy.reactive.jackson.deployment.processor; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; import javax.ws.rs.core.MediaType; -import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.AnnotationValue; -import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.DotName; -import org.jboss.jandex.Type; - -import com.fasterxml.jackson.annotation.JsonView; - import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem; import io.quarkus.resteasy.reactive.common.deployment.ServerDefaultProducesHandlerBuildItem; -import io.quarkus.resteasy.reactive.jackson.CustomSerialization; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyReader; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyWriter; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayMessageBodyReader; @@ -39,9 +23,6 @@ public class ResteasyReactiveJacksonProcessor { - private static final DotName JSON_VIEW = DotName.createSimple(JsonView.class.getName()); - private static final DotName CUSTOM_SERIALIZATION = DotName.createSimple(CustomSerialization.class.getName()); - @BuildStep void feature(BuildProducer feature) { feature.produce(new FeatureBuildItem(Feature.RESTEASY_REACTIVE_JACKSON)); @@ -52,6 +33,11 @@ ServerDefaultProducesHandlerBuildItem jsonDefault() { return ServerDefaultProducesHandlerBuildItem.json(); } + @BuildStep + ResteasyReactiveJacksonProviderDefinedBuildItem jacksonRegistered() { + return new ResteasyReactiveJacksonProviderDefinedBuildItem(); + } + @BuildStep void additionalProviders(BuildProducer additionalBean, BuildProducer additionalReaders, @@ -85,45 +71,4 @@ void additionalProviders(BuildProducer additionalBean, JsonObject.class.getName(), Collections.singletonList(MediaType.APPLICATION_JSON))); } - - @BuildStep - void handleJsonAnnotations(Optional resourceScanningResultBuildItem, - CombinedIndexBuildItem index, - BuildProducer reflectiveClass) { - if (!resourceScanningResultBuildItem.isPresent()) { - return; - } - Collection resourceClasses = resourceScanningResultBuildItem.get().getResult().getScannedResources() - .values(); - Set classesNeedingReflectionOnMethods = new HashSet<>(); - for (ClassInfo resourceClass : resourceClasses) { - DotName resourceClassDotName = resourceClass.name(); - if (resourceClass.annotations().containsKey(JSON_VIEW)) { - classesNeedingReflectionOnMethods.add(resourceClassDotName.toString()); - } else if (resourceClass.annotations().containsKey(CUSTOM_SERIALIZATION)) { - classesNeedingReflectionOnMethods.add(resourceClassDotName.toString()); - for (AnnotationInstance instance : resourceClass.annotations().get(CUSTOM_SERIALIZATION)) { - AnnotationValue annotationValue = instance.value(); - if (annotationValue != null) { - Type biFunctionType = annotationValue.asClass(); - ClassInfo biFunctionClassInfo = index.getIndex().getClassByName(biFunctionType.name()); - if (biFunctionClassInfo == null) { - // be lenient - } else { - if (!biFunctionClassInfo.hasNoArgsConstructor()) { - throw new RuntimeException( - "Class '" + biFunctionClassInfo.name() + "' must contain a no-args constructor"); - } - } - reflectiveClass.produce( - new ReflectiveClassBuildItem(true, false, false, biFunctionType.name().toString())); - } - } - } - } - if (!classesNeedingReflectionOnMethods.isEmpty()) { - reflectiveClass.produce( - new ReflectiveClassBuildItem(true, false, classesNeedingReflectionOnMethods.toArray(new String[0]))); - } - } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml index 8c3a873e527297..4810c69ea7cd47 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-resteasy-reactive-jackson Quarkus - RESTEasy Reactive - Jackson - Runtime - Jackson integration for RESTEasy Reactive. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. + Jackson serialization support for RESTEasy Reactive. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it @@ -20,11 +20,7 @@ io.quarkus - quarkus-jackson - - - io.quarkus - quarkus-jsonp + quarkus-resteasy-reactive-jackson-common diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyReader.java index a0558bf0506634..a13596a7192cd7 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyReader.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyReader.java @@ -12,20 +12,18 @@ import javax.ws.rs.core.Response; import org.jboss.resteasy.reactive.common.util.EmptyInputStream; -import org.jboss.resteasy.reactive.server.providers.serialisers.json.AbstractJsonMessageBodyReader; +import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; +import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.exc.MismatchedInputException; -public class JacksonMessageBodyReader extends AbstractJsonMessageBodyReader { - - private final ObjectReader reader; +public class JacksonMessageBodyReader extends JacksonBasicMessageBodyReader implements ServerMessageBodyReader { @Inject public JacksonMessageBodyReader(ObjectMapper mapper) { - this.reader = mapper.reader(); + super(mapper); } @Override @@ -38,6 +36,16 @@ public Object readFrom(Class type, Type genericType, Annotation[] annota } } + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isReadable(mediaType, type); + } + + @Override + public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, MediaType mediaType) { + return isReadable(mediaType, type); + } + @Override public Object readFrom(Class type, Type genericType, MediaType mediaType, ServerRequestContext context) throws WebApplicationException, IOException { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java index 566e5755e38316..57eda43c05c3d5 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java @@ -1,85 +1,37 @@ package io.quarkus.resteasy.reactive.jackson.runtime.serialisers; -import static org.jboss.resteasy.reactive.server.vertx.providers.serialisers.json.JsonMessageBodyWriterUtil.setContentTypeIfNecessary; +import static org.jboss.resteasy.reactive.server.vertx.providers.serialisers.json.JsonMessageServerBodyWriterUtil.setContentTypeIfNecessary; import java.io.IOException; import java.io.OutputStream; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; import java.util.function.Function; import javax.inject.Inject; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; import com.fasterxml.jackson.annotation.JsonView; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator.Feature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import io.quarkus.resteasy.reactive.jackson.CustomSerialization; -public class JacksonMessageBodyWriter extends ServerMessageBodyWriter.AllWriteableMessageBodyWriter { +public class JacksonMessageBodyWriter extends JacksonBasicMessageBodyWriter implements ServerMessageBodyWriter {// extends ServerMessageBodyWriter.AllWriteableMessageBodyWriter { private static final String JSON_VIEW_NAME = JsonView.class.getName(); private static final String CUSTOM_SERIALIZATION = CustomSerialization.class.getName(); - private final ObjectMapper originalMapper; - private final ObjectWriter defaultWriter; - private final ConcurrentMap perMethodWriter = new ConcurrentHashMap<>(); - @Inject public JacksonMessageBodyWriter(ObjectMapper mapper) { - this.originalMapper = mapper; - // we don't want the ObjectWriter to close the stream automatically, as we want to handle closing manually at the proper points - JsonFactory jsonFactory = mapper.getFactory(); - if (needsNewFactory(jsonFactory)) { - jsonFactory = jsonFactory.copy(); - setNecessaryJsonFactoryConfig(jsonFactory); - this.defaultWriter = mapper.writer().with(jsonFactory); - } else { - this.defaultWriter = mapper.writer(); - } - } - - private boolean needsNewFactory(JsonFactory jsonFactory) { - return jsonFactory.isEnabled(Feature.AUTO_CLOSE_TARGET) || jsonFactory.isEnabled(Feature.FLUSH_PASSED_TO_STREAM); - } - - private static void setNecessaryJsonFactoryConfig(JsonFactory jsonFactory) { - jsonFactory.configure(Feature.AUTO_CLOSE_TARGET, false); - jsonFactory.configure(Feature.FLUSH_PASSED_TO_STREAM, false); - } - - @Override - public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { - setContentTypeIfNecessary(httpHeaders); - if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - entityStream.write(((String) o).getBytes()); - } else { - if (annotations != null) { - for (Annotation annotation : annotations) { - if (JsonView.class.equals(annotation.annotationType())) { - if (handleJsonView(((JsonView) annotation), o, entityStream)) { - return; - } - } - } - } - entityStream.write(defaultWriter.writeValueAsBytes(o)); - } + super(mapper); } @Override @@ -113,6 +65,12 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte stream.close(); } + @Override + public final boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, + MediaType mediaType) { + return true; + } + // TODO: this can definitely be made faster if necessary by optimizing the use of the map and also by moving the creation of the // biFunction to build time private boolean handleCustomSerialization(Method method, Object o, Type genericType, OutputStream stream) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyReader.java index 1e99732ae9d90d..4de7d14949ad2c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyReader.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyReader.java @@ -1,50 +1,28 @@ package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import org.jboss.resteasy.reactive.common.providers.serialisers.PrimitiveBodyHandler; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; import io.vertx.core.json.JsonArray; -public class VertxJsonArrayMessageBodyReader extends PrimitiveBodyHandler implements ServerMessageBodyReader { +public class VertxJsonArrayMessageBodyReader extends VertxJsonArrayBasicMessageBodyReader + implements ServerMessageBodyReader { @Override public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, MediaType mediaType) { return isReadable(type); } - @Override - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return isReadable(type); - } - - private boolean isReadable(Class type) { - return JsonArray.class.isAssignableFrom(type); - } - @Override public JsonArray readFrom(Class type, Type genericType, MediaType mediaType, ServerRequestContext context) throws WebApplicationException, IOException { return readFrom(context.getInputStream()); } - - @Override - public JsonArray readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { - return readFrom(entityStream); - } - - private JsonArray readFrom(InputStream entityStream) throws IOException { - return new JsonArray(readFrom(entityStream, false)); - } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyWriter.java index 80b518d0b313e4..7f69733c4591c5 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonArrayMessageBodyWriter.java @@ -1,14 +1,10 @@ package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; import java.io.IOException; -import java.io.OutputStream; -import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter; @@ -16,31 +12,17 @@ import io.vertx.core.json.JsonArray; -public class VertxJsonArrayMessageBodyWriter implements ServerMessageBodyWriter { +public class VertxJsonArrayMessageBodyWriter extends VertxJsonArrayBasicMessageBodyWriter + implements ServerMessageBodyWriter { @Override public boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, MediaType mediaType) { return isWriteable(type); } - @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return isWriteable(type); - } - - private boolean isWriteable(Class type) { - return JsonArray.class.isAssignableFrom(type); - } - @Override public void writeResponse(JsonArray o, Type genericType, ServerRequestContext context) throws WebApplicationException, IOException { context.serverResponse().end(o.encode()); } - - @Override - public void writeTo(JsonArray o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { - entityStream.write(o.encode().getBytes(StandardCharsets.UTF_8)); - } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyReader.java index 49b1237984fa7c..2f57cdccb00e89 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyReader.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyReader.java @@ -1,50 +1,28 @@ package io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx; import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import org.jboss.resteasy.reactive.common.providers.serialisers.PrimitiveBodyHandler; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; import io.vertx.core.json.JsonObject; -public class VertxJsonObjectMessageBodyReader extends PrimitiveBodyHandler implements ServerMessageBodyReader { +public class VertxJsonObjectMessageBodyReader extends VertxJsonObjectBasicMessageBodyReader + implements ServerMessageBodyReader { @Override public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, MediaType mediaType) { return isReadable(type); } - @Override - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return isReadable(type); - } - - private boolean isReadable(Class type) { - return JsonObject.class.isAssignableFrom(type); - } - @Override public JsonObject readFrom(Class type, Type genericType, MediaType mediaType, ServerRequestContext context) throws WebApplicationException, IOException { return readFrom(context.getInputStream()); } - - @Override - public JsonObject readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { - return readFrom(entityStream); - } - - private JsonObject readFrom(InputStream entityStream) throws IOException { - return new JsonObject(readFrom(entityStream, false)); - } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyWriter.java index e656b170402ffc..fab40454691fe4 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/vertx/VertxJsonObjectMessageBodyWriter.java @@ -16,25 +16,17 @@ import io.vertx.core.json.JsonObject; -public class VertxJsonObjectMessageBodyWriter implements ServerMessageBodyWriter { +public class VertxJsonObjectMessageBodyWriter extends VertxJsonObjectBasicMessageBodyWriter + implements ServerMessageBodyWriter { @Override public boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, MediaType mediaType) { return isWriteable(type); } - @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return isWriteable(type); - } - - private boolean isWriteable(Class type) { - return JsonObject.class.isAssignableFrom(type); - } - @Override public void writeResponse(JsonObject o, Type genericType, ServerRequestContext context) - throws WebApplicationException, IOException { + throws WebApplicationException { context.serverResponse().end(o.encode()); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseResource.java index 45526efd9d9d65..3a3d4619a81d0c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseResource.java @@ -98,4 +98,11 @@ public Multi multiJson() { return Multi.createFrom().items(new Message("hello"), new Message("stef")); } + @Path("json/multi2") + @GET + @Produces(MediaType.SERVER_SENT_EVENTS) + public Multi multiDefaultElementType() { + return Multi.createFrom().items(new Message("hello"), new Message("stef")); + } + } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseTestCase.java index cc4abb6b1ed4a5..f0e76b4f1946d0 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/sse/SseTestCase.java @@ -103,6 +103,11 @@ public void testJsonMultiFromMulti() { testJsonMulti("sse/json/multi"); } + @Test + public void testJsonMultiFromMultiWithDefaultElementType() { + testJsonMulti("sse/json/multi2"); + } + private void testJsonMulti(String path) { Client client = ClientBuilder.newBuilder().build(); WebTarget target = client.target(uri.toString() + path); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml index 806eb660cb514d..fe85ce1a97913e 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-resteasy-reactive-jsonb Quarkus - RESTEasy Reactive - JSON-B - Runtime - JSON-B integration for RESTEasy Reactive. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. + JSON-B serialization support for RESTEasy Reactive. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyReader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyReader.java index e96bca33ba9065..af1c6e71aec78e 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyReader.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyReader.java @@ -11,11 +11,13 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import org.jboss.resteasy.reactive.common.providers.serialisers.AbstractJsonMessageBodyReader; import org.jboss.resteasy.reactive.common.util.EmptyInputStream; -import org.jboss.resteasy.reactive.server.providers.serialisers.json.AbstractJsonMessageBodyReader; +import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; +import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; -public class JsonbMessageBodyReader extends AbstractJsonMessageBodyReader { +public class JsonbMessageBodyReader extends AbstractJsonMessageBodyReader implements ServerMessageBodyReader { private final Jsonb json; @@ -36,6 +38,16 @@ public Object readFrom(Class type, Type genericType, MediaType mediaType return doReadFrom(type, genericType, context.getInputStream()); } + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isReadable(mediaType, type); + } + + @Override + public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, MediaType mediaType) { + return isReadable(mediaType, type); + } + private Object doReadFrom(Class type, Type genericType, InputStream entityStream) { if (entityStream instanceof EmptyInputStream) { return null; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java index 93bbbf7aaf8bb8..0b06f386439f0f 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java @@ -1,7 +1,5 @@ package io.quarkus.resteasy.reactive.jsonb.runtime.serialisers; -import static org.jboss.resteasy.reactive.server.vertx.providers.serialisers.json.JsonMessageBodyWriterUtil.*; - import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; @@ -13,8 +11,10 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import org.jboss.resteasy.reactive.common.providers.serialisers.JsonMessageBodyWriterUtil; import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter; import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; +import org.jboss.resteasy.reactive.server.vertx.providers.serialisers.json.JsonMessageServerBodyWriterUtil; public class JsonbMessageBodyWriter extends ServerMessageBodyWriter.AllWriteableMessageBodyWriter { @@ -28,7 +28,7 @@ public JsonbMessageBodyWriter(Jsonb json) { @Override public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { - setContentTypeIfNecessary(httpHeaders); + JsonMessageBodyWriterUtil.setContentTypeIfNecessary(httpHeaders); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... entityStream.write(((String) o).getBytes()); } else { @@ -39,7 +39,7 @@ public void writeTo(Object o, Class type, Type genericType, Annotation[] anno @Override public void writeResponse(Object o, Type genericType, ServerRequestContext context) throws WebApplicationException, IOException { - setContentTypeIfNecessary(context); + JsonMessageServerBodyWriterUtil.setContentTypeIfNecessary(context); OutputStream originalStream = context.getOrCreateOutputStream(); OutputStream stream = new NoopCloseAndFlushOutputStream(originalStream); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/pom.xml new file mode 100644 index 00000000000000..d22a41c9656d3b --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/pom.xml @@ -0,0 +1,58 @@ + + + + quarkus-resteasy-reactive-links-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-resteasy-reactive-links-deployment + Quarkus - RESTEasy Reactive - Links - Deployment + + + + io.quarkus + quarkus-resteasy-reactive-links + + + io.quarkus + quarkus-resteasy-reactive-deployment + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + true + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterAccessorImplementor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterAccessorImplementor.java new file mode 100644 index 00000000000000..b672f97ba0a330 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterAccessorImplementor.java @@ -0,0 +1,29 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import static io.quarkus.gizmo.MethodDescriptor.ofMethod; + +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.ResultHandle; +import io.quarkus.resteasy.reactive.links.runtime.GetterAccessor; + +class GetterAccessorImplementor { + + /** + * Implements a {@link GetterAccessor} that knows how to access a specific getter method of a specific type. + */ + void implement(ClassOutput classOutput, GetterMetadata getterMetadata) { + ClassCreator classCreator = ClassCreator.builder() + .classOutput(classOutput).className(getterMetadata.getGetterAccessorName()) + .interfaces(GetterAccessor.class) + .build(); + MethodCreator methodCreator = classCreator.getMethodCreator("get", Object.class, Object.class); + ResultHandle value = methodCreator.invokeVirtualMethod( + ofMethod(getterMetadata.getEntityType(), getterMetadata.getGetterName(), getterMetadata.getFieldType()), + methodCreator.getMethodParam(0)); + methodCreator.returnValue(value); + methodCreator.close(); + classCreator.close(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterImplementor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterImplementor.java new file mode 100644 index 00000000000000..670c0c8498990a --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterImplementor.java @@ -0,0 +1,54 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import static io.quarkus.gizmo.DescriptorUtils.objectToDescriptor; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.GETFIELD; +import static org.objectweb.asm.Opcodes.IRETURN; + +import java.util.function.BiFunction; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +import io.quarkus.gizmo.Gizmo; + +class GetterImplementor extends ClassVisitor { + + private final GetterMetadata getterMetadata; + + static BiFunction getVisitorFunction(GetterMetadata getterMetadata) { + return (className, classVisitor) -> new GetterImplementor(classVisitor, getterMetadata); + } + + GetterImplementor(ClassVisitor outputClassVisitor, GetterMetadata getterMetadata) { + super(Gizmo.ASM_API_VERSION, outputClassVisitor); + this.getterMetadata = getterMetadata; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, + String[] exceptions) { + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + + @Override + public void visitEnd() { + generateGetter(); + super.visitEnd(); + } + + private void generateGetter() { + String owner = Type.getType(objectToDescriptor(getterMetadata.getEntityType())).getInternalName(); + String fieldDescriptor = objectToDescriptor(getterMetadata.getFieldType()); + String getterDescriptor = "()" + fieldDescriptor; + + MethodVisitor mv = super.visitMethod(ACC_PUBLIC, getterMetadata.getGetterName(), getterDescriptor, null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, owner, getterMetadata.getFieldName(), fieldDescriptor); + mv.visitInsn(Type.getType(fieldDescriptor).getOpcode(IRETURN)); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterMetadata.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterMetadata.java new file mode 100644 index 00000000000000..733d7d23a11c59 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/GetterMetadata.java @@ -0,0 +1,63 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import java.util.Objects; + +import org.jboss.jandex.FieldInfo; + +class GetterMetadata { + + private static final String GETTER_PREFIX = "resteasy_links_get_"; + + private static final String ACCESSOR_SUFFIX = "$_resteasy_links"; + + private final String entityType; + + private final String fieldType; + + private final String fieldName; + + GetterMetadata(FieldInfo fieldInfo) { + this.entityType = fieldInfo.declaringClass().toString(); + this.fieldType = fieldInfo.type().name().toString(); + this.fieldName = fieldInfo.name(); + } + + public String getEntityType() { + return entityType; + } + + public String getFieldType() { + return fieldType; + } + + public String getFieldName() { + return fieldName; + } + + public String getGetterName() { + return GETTER_PREFIX + fieldName; + } + + public String getGetterAccessorName() { + return entityType + ACCESSOR_SUFFIX + getGetterName(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GetterMetadata that = (GetterMetadata) o; + return entityType.equals(that.entityType) + && fieldType.equals(that.fieldType) + && fieldName.equals(that.fieldName); + } + + @Override + public int hashCode() { + return Objects.hash(entityType, fieldType, fieldName); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java new file mode 100644 index 00000000000000..12c0c3eb196e2e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java @@ -0,0 +1,138 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.ws.rs.core.UriBuilder; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; +import org.jboss.resteasy.reactive.common.model.MethodParameter; +import org.jboss.resteasy.reactive.common.model.ResourceClass; +import org.jboss.resteasy.reactive.common.model.ResourceMethod; +import org.jboss.resteasy.reactive.common.util.URLUtils; + +import io.quarkus.resteasy.reactive.links.RestLink; +import io.quarkus.resteasy.reactive.links.runtime.LinkInfo; +import io.quarkus.resteasy.reactive.links.runtime.LinksContainer; + +final class LinksContainerFactory { + + private static final DotName REST_LINK_ANNOTATION = DotName.createSimple(RestLink.class.getName()); + + private final IndexView index; + + LinksContainerFactory(IndexView index) { + this.index = index; + } + + /** + * Find the resource methods that are marked with a {@link RestLink} annotations and add them to a links container. + */ + LinksContainer getLinksContainer(List resourceClasses) { + LinksContainer linksContainer = new LinksContainer(); + + for (ResourceClass resourceClass : resourceClasses) { + for (ResourceMethod resourceMethod : resourceClass.getMethods()) { + MethodInfo resourceMethodInfo = getResourceMethodInfo(resourceClass, resourceMethod); + AnnotationInstance restLinkAnnotation = resourceMethodInfo.annotation(REST_LINK_ANNOTATION); + if (restLinkAnnotation != null) { + LinkInfo linkInfo = getLinkInfo(resourceClass, resourceMethod, resourceMethodInfo, + restLinkAnnotation); + linksContainer.put(linkInfo); + } + } + } + + return linksContainer; + } + + private LinkInfo getLinkInfo(ResourceClass resourceClass, ResourceMethod resourceMethod, + MethodInfo resourceMethodInfo, AnnotationInstance restLinkAnnotation) { + String rel = getAnnotationValue(restLinkAnnotation, "rel", resourceMethod.getName()); + String entityType = getAnnotationValue(restLinkAnnotation, "entityType", deductEntityType(resourceMethodInfo)); + String path = UriBuilder.fromPath(resourceClass.getPath()).path(resourceMethod.getPath()).toTemplate(); + while (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + Set pathParameters = getPathParameters(path); + + return new LinkInfo(rel, entityType, path, pathParameters); + } + + /** + * If a method return type is parameterized and has a single argument (e.g. List), then use that argument as an + * entity type. Otherwise, use the return type. + */ + private String deductEntityType(MethodInfo methodInfo) { + if (methodInfo.returnType().kind() == Type.Kind.PARAMETERIZED_TYPE) { + if (methodInfo.returnType().asParameterizedType().arguments().size() == 1) { + return methodInfo.returnType().asParameterizedType().arguments().get(0).name().toString(); + } + } + return methodInfo.returnType().name().toString(); + } + + /** + * Extract parameters from a path string + */ + private Set getPathParameters(String path) { + Set names = new HashSet<>(); + URLUtils.parsePathParameters(path, names); + Set trimmedNames = new HashSet<>(names.size()); + for (String name : names) { + trimmedNames.add(name.trim()); + } + return trimmedNames; + } + + /** + * Find a {@link MethodInfo} for a given resource method + */ + private MethodInfo getResourceMethodInfo(ResourceClass resourceClass, ResourceMethod resourceMethod) { + ClassInfo classInfo = index.getClassByName(DotName.createSimple(resourceClass.getClassName())); + for (MethodInfo methodInfo : classInfo.methods()) { + if (isSameMethod(methodInfo, resourceMethod)) { + return methodInfo; + } + } + throw new RuntimeException(String.format("Could not find method info for resource '%s.%s'", + resourceClass.getClassName(), resourceMethod.getName())); + } + + /** + * Check if the given {@link MethodInfo} and {@link ResourceMethod} defined the same underlying method + */ + private boolean isSameMethod(MethodInfo resourceMethodInfo, ResourceMethod resourceMethod) { + if (!resourceMethodInfo.name().equals(resourceMethod.getName())) { + return false; + } + if (resourceMethodInfo.parameters().size() != resourceMethod.getParameters().length) { + return false; + } + + List parameterTypes = resourceMethodInfo.parameters(); + MethodParameter[] parameters = resourceMethod.getParameters(); + for (int i = 0; i < parameters.length; i++) { + if (!parameterTypes.get(i).name().equals(DotName.createSimple(parameters[i].type))) { + return false; + } + } + + return true; + } + + private String getAnnotationValue(AnnotationInstance annotationInstance, String name, String defaultValue) { + AnnotationValue value = annotationInstance.value(name); + if (value == null || value.asString().equals("")) { + return defaultValue; + } + return value.asString(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java new file mode 100644 index 00000000000000..3f8983032c57e2 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java @@ -0,0 +1,147 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.GeneratedClassGizmoAdaptor; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.resteasy.reactive.links.RestLinksResponseFilter; +import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainer; +import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainerRecorder; +import io.quarkus.resteasy.reactive.links.runtime.LinkInfo; +import io.quarkus.resteasy.reactive.links.runtime.LinksContainer; +import io.quarkus.resteasy.reactive.links.runtime.LinksProviderRecorder; +import io.quarkus.resteasy.reactive.links.runtime.RestLinksProviderProducer; +import io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveDeploymentInfoBuildItem; +import io.quarkus.resteasy.reactive.spi.CustomContainerResponseFilterBuildItem; +import io.quarkus.runtime.RuntimeValue; + +final class LinksProcessor { + + private final GetterAccessorImplementor getterAccessorImplementor = new GetterAccessorImplementor(); + + @BuildStep + void feature(BuildProducer feature) { + feature.produce(new FeatureBuildItem(Feature.RESTEASY_REACTIVE_LINKS)); + } + + @BuildStep + @Record(STATIC_INIT) + void initializeLinksProvider(CombinedIndexBuildItem indexBuildItem, + ResteasyReactiveDeploymentInfoBuildItem deploymentInfoBuildItem, + BuildProducer bytecodeTransformersProducer, + BuildProducer generatedClassesProducer, + GetterAccessorsContainerRecorder getterAccessorsContainerRecorder, + LinksProviderRecorder linksProviderRecorder) { + IndexView index = indexBuildItem.getIndex(); + ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClassesProducer, true); + + // Initialize links container + LinksContainer linksContainer = getLinksContainer(index, deploymentInfoBuildItem); + // Implement getters to access link path parameter values + RuntimeValue getterAccessorsContainer = implementPathParameterValueGetters( + index, classOutput, linksContainer, getterAccessorsContainerRecorder, bytecodeTransformersProducer); + + linksProviderRecorder.setGetterAccessorsContainer(getterAccessorsContainer); + linksProviderRecorder.setLinksContainer(linksContainer); + } + + @BuildStep + AdditionalBeanBuildItem registerRestLinksProviderProducer() { + return AdditionalBeanBuildItem.unremovableOf(RestLinksProviderProducer.class); + } + + @BuildStep + CustomContainerResponseFilterBuildItem registerRestLinksResponseFilter() { + return new CustomContainerResponseFilterBuildItem(RestLinksResponseFilter.class.getName()); + } + + private LinksContainer getLinksContainer(IndexView index, + ResteasyReactiveDeploymentInfoBuildItem deploymentInfoBuildItem) { + LinksContainerFactory linksContainerFactory = new LinksContainerFactory(index); + return linksContainerFactory.getLinksContainer( + deploymentInfoBuildItem.getDeploymentInfo().getResourceClasses()); + } + + /** + * For each path parameter implement a getter method in a class that holds its value. + * Then implement a getter accessor class that knows how to access that getter method to avoid using reflection later. + */ + private RuntimeValue implementPathParameterValueGetters(IndexView index, + ClassOutput classOutput, LinksContainer linksContainer, + GetterAccessorsContainerRecorder getterAccessorsContainerRecorder, + BuildProducer bytecodeTransformersProducer) { + RuntimeValue getterAccessorsContainer = getterAccessorsContainerRecorder.newContainer(); + Set implementedGetters = new HashSet<>(); + + for (List linkInfos : linksContainer.getLinksMap().values()) { + for (LinkInfo linkInfo : linkInfos) { + String entityType = linkInfo.getEntityType(); + for (String parameterName : linkInfo.getPathParameters()) { + // We implement a getter inside a class that has the required field. + // We later map that getter's accessor with a entity type. + // If a field is inside a parent class, the getter accessor will be mapped to each subclass which + // has REST links that need access to that field. + FieldInfo fieldInfo = getFieldInfo(index, DotName.createSimple(entityType), parameterName); + GetterMetadata getterMetadata = new GetterMetadata(fieldInfo); + if (!implementedGetters.contains(getterMetadata)) { + implementGetterWithAccessor(classOutput, bytecodeTransformersProducer, getterMetadata); + implementedGetters.add(getterMetadata); + } + + getterAccessorsContainerRecorder.addAccessor(getterAccessorsContainer, + entityType, parameterName, getterMetadata.getGetterAccessorName()); + } + } + } + + return getterAccessorsContainer; + } + + /** + * Implement a field getter inside a class and create an accessor class which knows how to access it. + */ + private void implementGetterWithAccessor(ClassOutput classOutput, + BuildProducer bytecodeTransformersProducer, + GetterMetadata getterMetadata) { + bytecodeTransformersProducer.produce(new BytecodeTransformerBuildItem( + getterMetadata.getEntityType(), GetterImplementor.getVisitorFunction(getterMetadata))); + getterAccessorImplementor.implement(classOutput, getterMetadata); + } + + /** + * Find a field info by name inside a class. + * This is a recursive method that looks through the class hierarchy until the field throws an error if it's not. + */ + private FieldInfo getFieldInfo(IndexView index, DotName className, String fieldName) { + ClassInfo classInfo = index.getClassByName(className); + if (classInfo == null) { + throw new RuntimeException(String.format("Class '%s' was not found", className)); + } + FieldInfo fieldInfo = classInfo.field(fieldName); + if (fieldInfo != null) { + return fieldInfo; + } + if (classInfo.superName() != null) { + return getFieldInfo(index, classInfo.superName(), fieldName); + } + throw new RuntimeException(String.format("Class '%s' field '%s' was not found", className, fieldName)); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/AbstractEntity.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/AbstractEntity.java new file mode 100644 index 00000000000000..861ad583483038 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/AbstractEntity.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +public abstract class AbstractEntity { + + private int id; + + private String slug; + + public AbstractEntity() { + } + + protected AbstractEntity(int id, String slug) { + this.id = id; + this.slug = slug; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getSlug() { + return slug; + } + + public void setSlug(String slug) { + this.slug = slug; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/RestLinksInjectionTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/RestLinksInjectionTest.java new file mode 100644 index 00000000000000..b5ed6b73ca37fd --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/RestLinksInjectionTest.java @@ -0,0 +1,103 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import static io.restassured.RestAssured.when; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import javax.ws.rs.core.Link; +import javax.ws.rs.core.UriBuilder; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class RestLinksInjectionTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(AbstractEntity.class, TestRecord.class, TestResource.class)); + + @TestHTTPResource("records") + String recordsUrl; + + @TestHTTPResource("records/without-links") + String recordsWithoutLinksUrl; + + @Test + void shouldGetById() { + List firstRecordLinks = when().get(recordsUrl + "/1") + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(firstRecordLinks).containsOnly( + Link.fromUri(recordsUrl).rel("list").build().toString(), + Link.fromUri(recordsWithoutLinksUrl).rel("getAllWithoutLinks").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/1")).rel("self").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/first")).rel("getBySlug").build().toString()); + + List secondRecordLinks = when().get(recordsUrl + "/2") + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(secondRecordLinks).containsOnly( + Link.fromUri(recordsUrl).rel("list").build().toString(), + Link.fromUri(recordsWithoutLinksUrl).rel("getAllWithoutLinks").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/2")).rel("self").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/second")) + .rel("getBySlug") + .build() + .toString()); + } + + @Test + void shouldGetBySlug() { + List firstRecordLinks = when().get(recordsUrl + "/first") + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(firstRecordLinks).containsOnly( + Link.fromUri(recordsUrl).rel("list").build().toString(), + Link.fromUri(recordsWithoutLinksUrl).rel("getAllWithoutLinks").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/1")).rel("self").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/first")).rel("getBySlug").build().toString()); + + List secondRecordLinks = when().get(recordsUrl + "/second") + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(secondRecordLinks).containsOnly( + Link.fromUri(recordsUrl).rel("list").build().toString(), + Link.fromUri(recordsWithoutLinksUrl).rel("getAllWithoutLinks").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/2")).rel("self").build().toString(), + Link.fromUriBuilder(UriBuilder.fromUri(recordsUrl).path("/second")) + .rel("getBySlug") + .build() + .toString()); + } + + @Test + void shouldGetAll() { + List links = when().get(recordsUrl) + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(links).containsOnly( + Link.fromUri(recordsUrl).rel("list").build().toString(), + Link.fromUri(recordsWithoutLinksUrl).rel("getAllWithoutLinks").build().toString()); + } + + @Test + void shouldGetAllWithoutLinks() { + List links = when().get(recordsWithoutLinksUrl) + .thenReturn() + .getHeaders() + .getValues("Link"); + assertThat(links).isEmpty(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestRecord.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestRecord.java new file mode 100644 index 00000000000000..1da9e9cf8ec52c --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestRecord.java @@ -0,0 +1,22 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +public class TestRecord extends AbstractEntity { + + private String value; + + public TestRecord() { + } + + public TestRecord(int id, String slug, String value) { + super(id, slug); + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestResource.java new file mode 100644 index 00000000000000..d5102e3fdbd082 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/test/java/io/quarkus/resteasy/reactive/links/deployment/TestResource.java @@ -0,0 +1,67 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import io.quarkus.resteasy.reactive.links.InjectRestLinks; +import io.quarkus.resteasy.reactive.links.RestLink; +import io.quarkus.resteasy.reactive.links.RestLinkType; + +@Path("/records") +public class TestResource { + + private static final AtomicInteger ID_COUNTER = new AtomicInteger(0); + + private static final List RECORDS = new LinkedList<>(Arrays.asList( + new TestRecord(ID_COUNTER.incrementAndGet(), "first", "First value"), + new TestRecord(ID_COUNTER.incrementAndGet(), "second", "Second value"))); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @RestLink(entityType = TestRecord.class, rel = "list") + @InjectRestLinks + public List getAll() { + return RECORDS; + } + + @GET + @Path("/without-links") + @Produces(MediaType.APPLICATION_JSON) + @RestLink + public List getAllWithoutLinks() { + return RECORDS; + } + + @GET + @Path("/{id: \\d+}") + @Produces(MediaType.APPLICATION_JSON) + @RestLink(entityType = TestRecord.class, rel = "self") + @InjectRestLinks(RestLinkType.INSTANCE) + public TestRecord getById(@PathParam("id") int id) { + return RECORDS.stream() + .filter(record -> record.getId() == id) + .findFirst() + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{slug: [a-zA-Z-]+}") + @Produces(MediaType.APPLICATION_JSON) + @RestLink + @InjectRestLinks(RestLinkType.INSTANCE) + public TestRecord getBySlug(@PathParam("slug") String slug) { + return RECORDS.stream() + .filter(record -> record.getSlug().equals(slug)) + .findFirst() + .orElseThrow(NotFoundException::new); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/pom.xml new file mode 100644 index 00000000000000..621f1d660d263a --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/pom.xml @@ -0,0 +1,21 @@ + + + + quarkus-resteasy-reactive-parent-aggregator + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-resteasy-reactive-links-parent + Quarkus - RESTEasy Reactive - Links + pom + + + deployment + runtime + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/pom.xml new file mode 100644 index 00000000000000..c18b1adab47984 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/pom.xml @@ -0,0 +1,43 @@ + + + + quarkus-resteasy-reactive-links-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-resteasy-reactive-links + Quarkus - RESTEasy Reactive - Links - Runtime + Quarkus RESTEast Reactive resource linking support + + + + io.quarkus + quarkus-resteasy-reactive + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/InjectRestLinks.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/InjectRestLinks.java new file mode 100644 index 00000000000000..ca8ca0f0ed2891 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/InjectRestLinks.java @@ -0,0 +1,13 @@ +package io.quarkus.resteasy.reactive.links; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +public @interface InjectRestLinks { + + RestLinkType value() default RestLinkType.TYPE; +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLink.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLink.java new file mode 100644 index 00000000000000..6f6b836a3d9ce5 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLink.java @@ -0,0 +1,15 @@ +package io.quarkus.resteasy.reactive.links; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RestLink { + + String rel() default ""; + + Class entityType() default Object.class; +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinkType.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinkType.java new file mode 100644 index 00000000000000..5a23ed33ca8216 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinkType.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.links; + +public enum RestLinkType { + TYPE, + INSTANCE +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksProvider.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksProvider.java new file mode 100644 index 00000000000000..ea0a90c331810d --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksProvider.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.links; + +import java.util.Collection; + +import javax.ws.rs.core.Link; + +public interface RestLinksProvider { + + Collection getTypeLinks(Class elementType); + + Collection getInstanceLinks(T instance); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java new file mode 100644 index 00000000000000..09b275d0d7a15a --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java @@ -0,0 +1,76 @@ +package io.quarkus.resteasy.reactive.links; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; + +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Link; + +import org.jboss.resteasy.reactive.server.ServerResponseFilter; +import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; + +public class RestLinksResponseFilter { + + private final RestLinksProvider linksProvider; + + public RestLinksResponseFilter(RestLinksProvider linksProvider) { + this.linksProvider = linksProvider; + } + + @ServerResponseFilter + public void filter(ResourceInfo resourceInfo, ContainerResponseContext responseContext) { + if (!(resourceInfo instanceof ResteasyReactiveResourceInfo)) { + return; + } + for (Link link : getLinks((ResteasyReactiveResourceInfo) resourceInfo, responseContext)) { + responseContext.getHeaders().add("Link", link); + } + } + + private Collection getLinks(ResteasyReactiveResourceInfo resourceInfo, + ContainerResponseContext responseContext) { + InjectRestLinks injectRestLinksAnnotation = getInjectRestLinksAnnotation(resourceInfo); + if (injectRestLinksAnnotation == null) { + return Collections.emptyList(); + } + + if (injectRestLinksAnnotation.value() == RestLinkType.INSTANCE && responseContext.hasEntity()) { + return linksProvider.getInstanceLinks(responseContext.getEntity()); + } + + return linksProvider.getTypeLinks(getEntityType(resourceInfo, responseContext)); + } + + private InjectRestLinks getInjectRestLinksAnnotation(ResteasyReactiveResourceInfo resourceInfo) { + if (resourceInfo.getMethodAnnotationNames().contains(InjectRestLinks.class.getName())) { + for (Annotation annotation : resourceInfo.getAnnotations()) { + if (annotation instanceof InjectRestLinks) { + return (InjectRestLinks) annotation; + } + } + } + if (resourceInfo.getClassAnnotationNames().contains(InjectRestLinks.class.getName())) { + for (Annotation annotation : resourceInfo.getClassAnnotations()) { + if (annotation instanceof InjectRestLinks) { + return (InjectRestLinks) annotation; + } + } + } + return null; + } + + private Class getEntityType(ResteasyReactiveResourceInfo resourceInfo, + ContainerResponseContext responseContext) { + for (Annotation annotation : resourceInfo.getAnnotations()) { + if (annotation instanceof RestLink) { + Class entityType = ((RestLink) annotation).entityType(); + if (entityType != Object.class) { + return entityType; + } + } + } + return responseContext.getEntityClass(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessor.java new file mode 100644 index 00000000000000..4028f7830fccd8 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessor.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +/** + * An accessor that knows how to access a specific getter method of a specific type. + */ +public interface GetterAccessor { + + /** + * Access a getter on a given instance and return a response. + */ + Object get(Object instance); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainer.java new file mode 100644 index 00000000000000..db1f8582729120 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainer.java @@ -0,0 +1,26 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class that allows us to easily find a {@code GetterAccessor} based on a type and a field name. + */ +public class GetterAccessorsContainer { + + private final Map> getterAccessors = new HashMap<>(); + + public GetterAccessor get(String className, String fieldName) { + return getterAccessors.get(className).get(fieldName); + } + + public void put(String className, String fieldName, GetterAccessor getterAccessor) { + if (!getterAccessors.containsKey(className)) { + getterAccessors.put(className, new HashMap<>()); + } + Map getterAccessorsByField = getterAccessors.get(className); + if (!getterAccessorsByField.containsKey(fieldName)) { + getterAccessorsByField.put(fieldName, getterAccessor); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainerRecorder.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainerRecorder.java new file mode 100644 index 00000000000000..6424690f18537d --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/GetterAccessorsContainerRecorder.java @@ -0,0 +1,33 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class GetterAccessorsContainerRecorder { + + /** + * Create new getter accessors container. + */ + public RuntimeValue newContainer() { + return new RuntimeValue<>(new GetterAccessorsContainer()); + } + + /** + * Add a getter accessor to a container. + */ + public void addAccessor(RuntimeValue container, String className, String fieldName, + String accessorName) { + try { + // Create a new accessor object early + GetterAccessor accessor = (GetterAccessor) Thread.currentThread() + .getContextClassLoader() + .loadClass(accessorName) + .getDeclaredConstructor() + .newInstance(); + container.getValue().put(className, fieldName, accessor); + } catch (Exception e) { + throw new RuntimeException("Failed to initialize " + accessorName + ": " + e.getMessage()); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinkInfo.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinkInfo.java new file mode 100644 index 00000000000000..06c8543c77eb9e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinkInfo.java @@ -0,0 +1,40 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import java.util.Set; + +import io.quarkus.runtime.annotations.RecordableConstructor; + +public final class LinkInfo { + + private final String rel; + + private final String entityType; + + private final String path; + + private final Set pathParameters; + + @RecordableConstructor + public LinkInfo(String rel, String entityType, String path, Set pathParameters) { + this.rel = rel; + this.entityType = entityType; + this.path = path; + this.pathParameters = pathParameters; + } + + public String getRel() { + return rel; + } + + public String getEntityType() { + return entityType; + } + + public String getPath() { + return path; + } + + public Set getPathParameters() { + return pathParameters; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksContainer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksContainer.java new file mode 100644 index 00000000000000..fd2d1d4f1ecb20 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksContainer.java @@ -0,0 +1,43 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import java.util.Collections; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; + +import org.jboss.resteasy.reactive.common.util.MultivaluedTreeMap; + +import io.quarkus.runtime.annotations.RecordableConstructor; + +/** + * A container holding links mapped by an entity which they represent. + */ +public final class LinksContainer { + + /** + * Links mapped by their entity type. + * In order to be recorded this field name has to match the constructor parameter name and have a getter. + */ + private final MultivaluedMap linksMap; + + public LinksContainer() { + linksMap = new MultivaluedTreeMap<>(); + } + + @RecordableConstructor + public LinksContainer(MultivaluedMap linksMap) { + this.linksMap = linksMap; + } + + public List getForClass(Class c) { + return linksMap.getOrDefault(c.getName(), Collections.emptyList()); + } + + public void put(LinkInfo linkInfo) { + linksMap.add(linkInfo.getEntityType(), linkInfo); + } + + public MultivaluedMap getLinksMap() { + return MultivaluedTreeMap.clone(linksMap); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksProviderRecorder.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksProviderRecorder.java new file mode 100644 index 00000000000000..286f8c4bb7b44e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/LinksProviderRecorder.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class LinksProviderRecorder { + + public void setLinksContainer(LinksContainer linksContainer) { + RestLinksProviderImpl.setLinksContainer(linksContainer); + } + + public void setGetterAccessorsContainer(RuntimeValue getterAccessorsContainer) { + RestLinksProviderImpl.setGetterAccessorsContainer(getterAccessorsContainer.getValue()); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java new file mode 100644 index 00000000000000..7a0354d4a6d74e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java @@ -0,0 +1,81 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import javax.ws.rs.core.Link; +import javax.ws.rs.core.UriInfo; + +import io.quarkus.resteasy.reactive.links.RestLinksProvider; + +final class RestLinksProviderImpl implements RestLinksProvider { + + private static LinksContainer linksContainer; + + private static GetterAccessorsContainer getterAccessorsContainer; + + private final UriInfo uriInfo; + + static void setLinksContainer(LinksContainer context) { + RestLinksProviderImpl.linksContainer = context; + } + + static void setGetterAccessorsContainer(GetterAccessorsContainer getterAccessorsContainer) { + RestLinksProviderImpl.getterAccessorsContainer = getterAccessorsContainer; + } + + RestLinksProviderImpl(UriInfo uriInfo) { + this.uriInfo = uriInfo; + } + + @Override + public Collection getTypeLinks(Class elementType) { + verifyInit(); + + List links = new LinkedList<>(); + for (LinkInfo linkInfo : linksContainer.getForClass(elementType)) { + if (linkInfo.getPathParameters().size() == 0) { + links.add(Link.fromUriBuilder(uriInfo.getBaseUriBuilder().path(linkInfo.getPath())) + .rel(linkInfo.getRel()) + .build()); + } + } + return links; + } + + @Override + public Collection getInstanceLinks(T instance) { + verifyInit(); + + List links = new LinkedList<>(); + for (LinkInfo linkInfo : linksContainer.getForClass(instance.getClass())) { + links.add(Link.fromUriBuilder(uriInfo.getBaseUriBuilder().path(linkInfo.getPath())) + .rel(linkInfo.getRel()) + .build(getPathParameterValues(linkInfo, instance))); + } + return links; + } + + private Object[] getPathParameterValues(LinkInfo linkInfo, Object instance) { + List values = new ArrayList<>(linkInfo.getPathParameters().size()); + for (String name : linkInfo.getPathParameters()) { + GetterAccessor accessor = getterAccessorsContainer.get(linkInfo.getEntityType(), name); + if (accessor == null) { + throw new RuntimeException("Could not get '" + name + "' value"); + } + values.add(accessor.get(instance)); + } + return values.toArray(); + } + + private void verifyInit() { + if (linksContainer == null) { + throw new IllegalStateException("Links context has not been initialized"); + } + if (getterAccessorsContainer == null) { + throw new IllegalStateException("Getter accessors container has not been initialized"); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderProducer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderProducer.java new file mode 100644 index 00000000000000..a2119687f984ec --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderProducer.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.links.runtime; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Produces; +import javax.ws.rs.core.UriInfo; + +import io.quarkus.arc.DefaultBean; +import io.quarkus.resteasy.reactive.links.RestLinksProvider; + +@Dependent +public final class RestLinksProviderProducer { + + @Produces + @RequestScoped + @DefaultBean + public RestLinksProvider restLinksProvider(UriInfo uriInfo) { + return new RestLinksProviderImpl(uriInfo); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..26fa8c254aadb9 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,12 @@ +--- +name: "RESTEasy Reactive Links" +metadata: + short-name: "resteasy-reactive-links" + keywords: + - "rest" + - "jaxrs" + - "links" + categories: + - "web" + - "reactive" + status: "experimental" diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/pom.xml new file mode 100644 index 00000000000000..ad5e69f0c2e875 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + quarkus-resteasy-reactive-problem-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-resteasy-reactive-problem-deployment + Quarkus - RESTEasy Reactive - Problem - Deployment + + + + io.quarkus + quarkus-resteasy-reactive-problem + ${project.version} + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-resteasy-reactive-common-deployment + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java new file mode 100644 index 00000000000000..2bb609980e13e7 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/main/java/io/quarkus/resteasy/reactive/problem/deployment/QuarkusResteasyReactiveProblemProcessor.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.problem.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class QuarkusResteasyReactiveProblemProcessor { + + private static final String FEATURE = "quarkus-resteasy-reactive-problem"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java new file mode 100644 index 00000000000000..25fdef13680e7a --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemDevModeTest.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.problem.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class QuarkusResteasyReactiveProblemDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java new file mode 100644 index 00000000000000..9bb7430c84a5a7 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/deployment/src/test/java/io/quarkus/resteasy/reactive/problem/test/QuarkusResteasyReactiveProblemTest.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.problem.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class QuarkusResteasyReactiveProblemTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/pom.xml new file mode 100644 index 00000000000000..7c403ffc4a7682 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/pom.xml @@ -0,0 +1,21 @@ + + + + quarkus-resteasy-reactive-parent-aggregator + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-resteasy-reactive-problem-parent + Quarkus - RESTEasy Reactive - Problem + pom + + + deployment + runtime + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/pom.xml new file mode 100644 index 00000000000000..ace7323e3c26d2 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + quarkus-resteasy-reactive-problem-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-resteasy-reactive-problem + Quarkus - RESTEasy Reactive - Problem - Runtime + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy-reactive-common + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..22fb2756cf9cd3 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-problem/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Quarkus Resteasy Reactive Problem +#description: Quarkus Resteasy Reactive Problem ... +metadata: +# keywords: +# - quarkus-resteasy-reactive-problem +# guide: ... +# categories: +# - "miscellaneous" +# status: "preview" \ No newline at end of file diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveDeploymentInfoBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveDeploymentInfoBuildItem.java new file mode 100644 index 00000000000000..c4bc3ec4cad468 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveDeploymentInfoBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import org.jboss.resteasy.reactive.server.core.DeploymentInfo; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class ResteasyReactiveDeploymentInfoBuildItem extends SimpleBuildItem { + + private final DeploymentInfo deploymentInfo; + + public ResteasyReactiveDeploymentInfoBuildItem(DeploymentInfo deploymentInfo) { + this.deploymentInfo = deploymentInfo; + } + + public DeploymentInfo getDeploymentInfo() { + return deploymentInfo; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 738d8c310fdbff..ccbefc71b26644 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -111,7 +111,9 @@ import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; import io.quarkus.resteasy.reactive.spi.JaxrsFeatureBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem; import io.quarkus.runtime.RuntimeValue; import io.quarkus.security.AuthenticationCompletionException; import io.quarkus.security.AuthenticationFailedException; @@ -144,7 +146,8 @@ CapabilityBuildItem capability() { @BuildStep void vertxIntegration(BuildProducer writerBuildItemBuildProducer) { writerBuildItemBuildProducer.produce(new MessageBodyWriterBuildItem(ServerVertxBufferMessageBodyWriter.class.getName(), - Buffer.class.getName(), Collections.singletonList(MediaType.WILDCARD), RuntimeType.SERVER, true)); + Buffer.class.getName(), Collections.singletonList(MediaType.WILDCARD), RuntimeType.SERVER, true, + Priorities.USER)); } @BuildStep @@ -232,10 +235,13 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem List dynamicFeatures, List additionalMessageBodyReaders, List additionalMessageBodyWriters, + List messageBodyReaderOverrideBuildItems, + List messageBodyWriterOverrideBuildItems, List features, List serverDefaultProducesHandlers, Optional requestContextFactoryBuildItem, Optional classLevelExceptionMappers, + BuildProducer quarkusRestDeploymentInfoBuildItemBuildProducer, BuildProducer quarkusRestDeploymentBuildItemBuildProducer, BuildProducer reflectiveClass, BuildProducer reflectiveHierarchy, @@ -296,6 +302,7 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem paramConverterProviders.initializeDefaultFactories(factoryFunction); paramConverterProviders.sort(); interceptors.sort(); + interceptors.getContainerRequestFilters().validateThreadModel(); try (ClassCreator c = new ClassCreator(new GeneratedClassGizmoAdaptor(generatedClassBuildItemBuildProducer, true), QUARKUS_INIT_CLASS, null, Object.class.getName(), ResteasyReactiveInitialiser.class.getName()); @@ -458,7 +465,8 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an ServerSerialisers serialisers = new ServerSerialisers(); SerializersUtil.setupSerializers(recorder, reflectiveClass, additionalMessageBodyReaders, - additionalMessageBodyWriters, beanContainerBuildItem, applicationResultBuildItem, serialisers, + additionalMessageBodyWriters, messageBodyReaderOverrideBuildItems, messageBodyWriterOverrideBuildItems, + beanContainerBuildItem, applicationResultBuildItem, serialisers, RuntimeType.SERVER); // built-ins @@ -503,7 +511,7 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an // Handler used for both the default and non-default deployment path (specified as application path or resteasyConfig.path) // Routes use the order VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1 to ensure the default route is called before the resteasy one Class applicationClass = application == null ? Application.class : application.getClass(); - RuntimeValue deployment = recorder.createDeployment(new DeploymentInfo() + DeploymentInfo deploymentInfo = new DeploymentInfo() .setInterceptors(interceptors.sort()) .setConfig(new org.jboss.resteasy.reactive.common.ResteasyReactiveConfig( config.inputBufferSize.asLongValue(), config.singleDefaultProduces, config.defaultProduces)) @@ -518,7 +526,11 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an .setApplicationPath(applicationPath) .setResourceClasses(resourceClasses) .setLocatableResourceClasses(subResourceClasses) - .setParamConverterProviders(paramConverterProviders), + .setParamConverterProviders(paramConverterProviders); + quarkusRestDeploymentInfoBuildItemBuildProducer + .produce(new ResteasyReactiveDeploymentInfoBuildItem(deploymentInfo)); + + RuntimeValue deployment = recorder.createDeployment(deploymentInfo, beanContainerBuildItem.getValue(), shutdownContext, vertxConfig, requestContextFactoryBuildItem.map(RequestContextFactoryBuildItem::getFactory).orElse(null), initClassFactory); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java index ba872454f21f64..b9951324713957 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java @@ -285,6 +285,10 @@ public void handleCustomAnnotatedMethods( if (preMatchingValue != null) { builder.setPreMatching(preMatchingValue.asBoolean()); } + AnnotationValue nonBlockingRequiredValue = instance.value("nonBlocking"); + if (nonBlockingRequiredValue != null) { + builder.setNonBlockingRequired(nonBlockingRequiredValue.asBoolean()); + } List annotations = methodInfo.annotations(); Set nameBindingNames = new HashSet<>(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html index c3f1c7e8920cbd..22e0b1d52ad2dc 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html @@ -221,6 +221,12 @@ {/if} +
+ Resource Class: +
+ diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/AnotherValidNonBlockingFiltersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/AnotherValidNonBlockingFiltersTest.java new file mode 100644 index 00000000000000..029b3e4e04d5c2 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/AnotherValidNonBlockingFiltersTest.java @@ -0,0 +1,176 @@ +package io.quarkus.resteasy.reactive.server.test.customproviders; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.function.Supplier; + +import javax.annotation.Priority; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.server.ServerRequestFilter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.BlockingOperationControl; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.Headers; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.common.annotation.NonBlocking; + +public class AnotherValidNonBlockingFiltersTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(StandardBlockingRequestFilter.class, AnotherStandardBlockingRequestFilter.class, + StandardNonBlockingRequestFilter.class, PreMatchingNonBlockingRequestFilter.class, + CustomFilters.class, + DummyResource.class); + } + }); + + @Test + public void testBlockingEndpoint() { + Headers headers = RestAssured.given().get("/dummy/blocking") + .then().statusCode(200).extract().headers(); + assertEquals( + "1-pre-matching-non-blocking/2-another-custom-non-blocking/3-standard-non-blocking/4-standard-blocking/5-another-standard-blocking/6-custom-blocking", + headers.get("filter-request").getValue()); + assertEquals( + "false/false/false/true/true/true", + headers.get("thread").getValue()); + } + + @Test + public void testNonBlockingEndpoint() { + Headers headers = RestAssured.given().get("/dummy/nonblocking") + .then().statusCode(200).extract().headers(); + assertEquals( + "1-pre-matching-non-blocking/2-another-custom-non-blocking/3-standard-non-blocking/4-standard-blocking/5-another-standard-blocking/6-custom-blocking", + headers.get("filter-request").getValue()); + assertEquals( + "false/false/false/false/false/false", + headers.get("thread").getValue()); + } + + @Blocking + @Path("dummy") + public static class DummyResource { + + @Path("blocking") + @GET + public Response blocking(@Context HttpHeaders headers) { + return getResponse(headers); + } + + @NonBlocking + @Path("nonblocking") + @GET + public Response nonblocking(@Context HttpHeaders headers) { + return getResponse(headers); + } + + private Response getResponse(HttpHeaders headers) { + return Response.ok() + .header("filter-request", headers.getHeaderString("filter-request")) + .header("thread", headers.getHeaderString("thread")) + .build(); + } + } + + @Provider + @PreMatching + @NonBlocking + public static class PreMatchingNonBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + headers.putSingle("filter-request", "1-pre-matching-non-blocking"); + headers.putSingle("thread", "" + BlockingOperationControl.isBlockingAllowed()); + } + } + + @Provider + @Priority(Priorities.USER + 100) + public static class StandardBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/4-standard-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + @Provider + @Priority(Priorities.USER + 200) + public static class AnotherStandardBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/5-another-standard-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + @Provider + @Priority(Priorities.USER + 50) + @NonBlocking + public static class StandardNonBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/3-standard-non-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + public static class CustomFilters { + + @ServerRequestFilter(nonBlocking = true, priority = Priorities.USER + 20) + public void anotherNonBlocking(ContainerRequestContext requestContext) { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", + previousFilterHeaderValue + "/2-another-custom-non-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + + @ServerRequestFilter(priority = Priorities.USER + 300) + public void blocking(ContainerRequestContext requestContext) { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/6-custom-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidNonBlockingFiltersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidNonBlockingFiltersTest.java new file mode 100644 index 00000000000000..331ccb6970cf77 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidNonBlockingFiltersTest.java @@ -0,0 +1,75 @@ +package io.quarkus.resteasy.reactive.server.test.customproviders; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.util.function.Supplier; + +import javax.annotation.Priority; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.common.annotation.NonBlocking; + +public class InvalidNonBlockingFiltersTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(StandardBlockingRequestFilter.class, StandardNonBlockingRequestFilter.class, + DummyResource.class); + } + }).assertException(t -> Assertions.assertTrue(t.getMessage().contains("StandardNonBlockingRequestFilter"))); + + @Test + public void test() { + fail("Should never have been called"); + } + + @Path("dummy") + public static class DummyResource { + + @Blocking + @Path("blocking") + @GET + public Response blocking() { + return Response.ok().build(); + } + } + + @Provider + public static class StandardBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + } + } + + @NonBlocking + @Provider + @Priority(Priorities.USER + 1) + public static class StandardNonBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ValidNonBlockingFiltersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ValidNonBlockingFiltersTest.java new file mode 100644 index 00000000000000..bea1da3764a289 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ValidNonBlockingFiltersTest.java @@ -0,0 +1,167 @@ +package io.quarkus.resteasy.reactive.server.test.customproviders; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.util.function.Supplier; + +import javax.annotation.Priority; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.server.ServerRequestFilter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.BlockingOperationControl; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.Headers; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.common.annotation.NonBlocking; + +public class ValidNonBlockingFiltersTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(StandardBlockingRequestFilter.class, AnotherStandardBlockingRequestFilter.class, + StandardNonBlockingRequestFilter.class, DummyResource.class); + } + }); + + @Test + public void testBlockingEndpoint() { + Headers headers = RestAssured.given().get("/dummy/blocking") + .then().statusCode(200).extract().headers(); + assertEquals( + "1-custom-non-blocking/2-another-custom-non-blocking/3-standard-non-blocking/4-standard-blocking/5-another-standard-blocking/6-custom-blocking", + headers.get("filter-request").getValue()); + assertEquals( + "false/false/false/true/true/true", + headers.get("thread").getValue()); + } + + @Test + public void testNonBlockingEndpoint() { + Headers headers = RestAssured.given().get("/dummy/nonblocking") + .then().statusCode(200).extract().headers(); + assertEquals( + "1-custom-non-blocking/2-another-custom-non-blocking/3-standard-non-blocking/4-standard-blocking/5-another-standard-blocking/6-custom-blocking", + headers.get("filter-request").getValue()); + assertEquals( + "false/false/false/false/false/false", + headers.get("thread").getValue()); + } + + @Path("dummy") + public static class DummyResource { + + @Blocking + @Path("blocking") + @GET + public Response blocking(@Context HttpHeaders headers) { + return getResponse(headers); + } + + @Path("nonblocking") + @GET + public Response nonblocking(@Context HttpHeaders headers) { + return getResponse(headers); + } + + private Response getResponse(HttpHeaders headers) { + return Response.ok() + .header("filter-request", headers.getHeaderString("filter-request")) + .header("thread", headers.getHeaderString("thread")) + .build(); + } + } + + @Provider + @Priority(Priorities.USER + 100) + public static class StandardBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/4-standard-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + @Provider + @Priority(Priorities.USER + 200) + public static class AnotherStandardBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/5-another-standard-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + @Provider + @Priority(Priorities.USER + 50) + @NonBlocking + public static class StandardNonBlockingRequestFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/3-standard-non-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + + public static class CustomFilters { + + @ServerRequestFilter(nonBlocking = true) + public void firstNonBlocking(ContainerRequestContext requestContext) { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", "1-custom-non-blocking"); + headers.putSingle("thread", "" + BlockingOperationControl.isBlockingAllowed()); + } + + @ServerRequestFilter(nonBlocking = true, priority = Priorities.USER + 20) + public void anotherNonBlocking(ContainerRequestContext requestContext) { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", + previousFilterHeaderValue + "/2-another-custom-non-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + + @ServerRequestFilter(priority = Priorities.USER + 300) + public void blocking(ContainerRequestContext requestContext) { + MultivaluedMap headers = requestContext.getHeaders(); + String previousFilterHeaderValue = headers.getFirst("filter-request"); + headers.putSingle("filter-request", previousFilterHeaderValue + "/6-custom-blocking"); + String previousThreadHeaderValue = headers.getFirst("thread"); + headers.putSingle("thread", previousThreadHeaderValue + "/" + BlockingOperationControl.isBlockingAllowed()); + } + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/ApplicationWithBlockingTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/ApplicationWithBlockingTest.java index 4abbe528c48a2c..ff4fca7bd61a86 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/ApplicationWithBlockingTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/ApplicationWithBlockingTest.java @@ -36,6 +36,12 @@ public void test() { RestAssured.get("/tname/nonblocking") .then().body(Matchers.containsString("loop"), Matchers.not(Matchers.containsString("executor"))); + + RestAssured.get("/tname2/blocking") + .then().body(Matchers.containsString("executor"), Matchers.not(Matchers.containsString("loop"))); + + RestAssured.get("/tname2/nonblocking") + .then().body(Matchers.containsString("loop"), Matchers.not(Matchers.containsString("executor"))); } @Blocking @@ -60,4 +66,22 @@ public String nonBlocking() { } } + @Blocking // this should have no effect + @Path("tname2") + public static class ThreadNameResourceWithBlocking { + + @Path("blocking") + @GET + public String threadName() { + return Thread.currentThread().getName(); + } + + @NonBlocking + @Path("nonblocking") + @GET + public String nonBlocking() { + return Thread.currentThread().getName(); + } + } + } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceWithImplTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceWithImplTest.java new file mode 100644 index 00000000000000..64a2e217ee7896 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceWithImplTest.java @@ -0,0 +1,67 @@ +package io.quarkus.resteasy.reactive.server.test.simple; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.hamcrest.Matchers; +import org.jboss.resteasy.reactive.server.core.BlockingOperationSupport; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.smallrye.common.annotation.Blocking; + +public class InterfaceWithImplTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(Greeting.class, GreetingImpl.class)); + + @Test + public void test() { + RestAssured.get("/hello/greeting/universe") + .then().body(Matchers.equalTo("name: universe / blocking: true")); + + RestAssured.get("/hello/greeting2/universe") + .then().body(Matchers.equalTo("name: universe / blocking: false")); + } + + @Path("/hello") + public interface Greeting { + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/greeting/{name}") + String greeting(String name); + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/greeting2/{name}") + String greeting2(String name); + } + + public static class GreetingImpl implements Greeting { + + @Override + @Blocking + public String greeting(String name) { + return resultString(name); + } + + @Override + public String greeting2(String name) { + return resultString(name); + } + + private String resultString(String name) { + return "name: " + name + " / blocking: " + BlockingOperationSupport.isBlockingAllowed(); + } + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/NewParamsRestResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/NewParamsRestResource.java index fcb4aa7203ca0f..b9e57109a60a92 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/NewParamsRestResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/NewParamsRestResource.java @@ -49,10 +49,16 @@ public String params(@RestPath String p, @RestQuery Optional q2, @RestQuery Optional q3, @RestHeader int h, + @RestHeader String xMyHeader, + @RestHeader("Test-Header-Param") String testHeaderParam, + @RestHeader("") String paramEmpty, @RestForm String f, @RestMatrix String m, @RestCookie String c) { - return "params: p: " + p + ", q: " + q + ", h: " + h + ", f: " + f + ", m: " + m + ", c: " + c + ", q2: " + return "params: p: " + p + ", q: " + q + ", h: " + h + ", xMyHeader: " + xMyHeader + ", testHeaderParam: " + + testHeaderParam + ", paramEmpty: " + + paramEmpty + ", f: " + f + ", m: " + m + ", c: " + + c + ", q2: " + q2.orElse("empty") + ", q3: " + q3.orElse(-1); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java index 0af962e3c270f2..ece6f57e522ceb 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java @@ -385,11 +385,16 @@ public void testNewParams() { .queryParam("q", "qv") .queryParam("q3", "999") .header("h", "123") + .header("X-My-Header", "test") + .header("Test-Header-Param", "test") + .header("Param-Empty", "empty") .formParam("f", "fv") .post("/new-params/myklass;m=mv/myregex/params/pv") .then() .log().ifError() - .body(Matchers.equalTo("params: p: pv, q: qv, h: 123, f: fv, m: mv, c: cv, q2: empty, q3: 999")); + .body(Matchers + .equalTo( + "params: p: pv, q: qv, h: 123, xMyHeader: test, testHeaderParam: test, paramEmpty: empty, f: fv, m: mv, c: cv, q2: empty, q3: 999")); RestAssured.get("/new-params/myklass/myregex/sse") .then() .log().ifError() diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 44cce2716d9f71..5b4dfa0e71239b 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -16,4 +16,4 @@ metadata: - "java" - "kotlin" - "scala" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/pom.xml new file mode 100644 index 00000000000000..79bcfdda5dc3e8 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + quarkus-rest-client-reactive-jackson-parent + io.quarkus + 999-SNAPSHOT + + quarkus-rest-client-reactive-jackson-deployment + Quarkus - MicroProfile Rest Client with Reactive RestEasy and Jackson - Deployment + + + io.quarkus + quarkus-resteasy-reactive-jackson-common-deployment + + + io.quarkus + quarkus-rest-client-reactive-deployment + + + io.quarkus + quarkus-rest-client-reactive-jackson + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java new file mode 100644 index 00000000000000..a81063fca8508b --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -0,0 +1,62 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.processor; + +import java.util.Collections; +import java.util.List; + +import javax.ws.rs.core.MediaType; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonBasicMessageBodyReader; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonBasicMessageBodyWriter; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayBasicMessageBodyReader; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayBasicMessageBodyWriter; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonObjectBasicMessageBodyReader; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonObjectBasicMessageBodyWriter; +import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; +import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +public class ResteasyReactiveJacksonProcessor { + + @BuildStep + void additionalProviders( + List jacksonProviderDefined, + BuildProducer additionalBean, + BuildProducer additionalReaders, + BuildProducer additionalWriters) { + if (jacksonProviderDefined != null && !jacksonProviderDefined.isEmpty()) { + return; + } + // make these beans to they can get instantiated with the Quarkus CDI configured Jsonb object + additionalBean.produce(AdditionalBeanBuildItem.builder() + .addBeanClass(JacksonBasicMessageBodyReader.class.getName()) + .addBeanClass(JacksonBasicMessageBodyWriter.class.getName()) + .setUnremovable().build()); + + additionalReaders + .produce(new MessageBodyReaderBuildItem(JacksonBasicMessageBodyReader.class.getName(), Object.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + additionalReaders + .produce(new MessageBodyReaderBuildItem(VertxJsonArrayBasicMessageBodyReader.class.getName(), + JsonArray.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + additionalReaders + .produce(new MessageBodyReaderBuildItem(VertxJsonObjectBasicMessageBodyReader.class.getName(), + JsonObject.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + additionalWriters + .produce(new MessageBodyWriterBuildItem(JacksonBasicMessageBodyWriter.class.getName(), Object.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + additionalWriters + .produce(new MessageBodyWriterBuildItem(VertxJsonArrayBasicMessageBodyWriter.class.getName(), + JsonArray.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + additionalWriters + .produce(new MessageBodyWriterBuildItem(VertxJsonObjectBasicMessageBodyWriter.class.getName(), + JsonObject.class.getName(), + Collections.singletonList(MediaType.APPLICATION_JSON))); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jackson/pom.xml new file mode 100644 index 00000000000000..75ff6df4939e87 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-resteasy-reactive-parent-aggregator + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-rest-client-reactive-jackson-parent + Quarkus - MicroProfile Rest Client with Reactive RestEasy and Jackson - Parent + pom + + deployment + runtime + + diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml new file mode 100644 index 00000000000000..8247acb53b1a02 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + quarkus-rest-client-reactive-jackson-parent + io.quarkus + 999-SNAPSHOT + + quarkus-rest-client-reactive-jackson + Quarkus - MicroProfile Rest Client with Reactive RESTEasy and Jackson + Jackson serialization support for RESTEasy Reactive Rest Client. + + + + io.quarkus + quarkus-resteasy-reactive-jackson-common + + + io.quarkus + quarkus-rest-client-reactive + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/resources/quarkus-extension.yaml b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/resources/quarkus-extension.yaml new file mode 100644 index 00000000000000..8c2b75eccaef29 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/resources/quarkus-extension.yaml @@ -0,0 +1,16 @@ +--- +name: "Reactive REST Client Jackson" +metadata: + keywords: + - "rest-client-jackson" + - "rest-client" + - "web-client" + - "microprofile-rest-client" + - "json" + - "jackson" + - "resteasy-reactive" + categories: + - "microprofile" + - "web" + - "serialization" + status: "experimental" diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/resources/quarkus-extension.yaml b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/resources/quarkus-extension.yaml index 4745aab1693018..dca7f8fa1f44cf 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/resources/quarkus-extension.yaml +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/resources/quarkus-extension.yaml @@ -1,7 +1,15 @@ --- -name: "MicroProfile Rest Client with RESTEasy Reactive" +name: "Reactive REST Client" metadata: keywords: + - "rest-client-jackson" + - "rest-client" + - "web-client" + - "microprofile-rest-client" + - "json" + - "jackson" + - "resteasy-reactive" categories: - "microprofile" + - "web" status: "experimental" diff --git a/extensions/scala/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/scala/runtime/src/main/resources/META-INF/quarkus-extension.yaml index b35474f9c4a46b..b4850dd17f50cc 100644 --- a/extensions/scala/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/scala/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,4 +8,4 @@ metadata: codestart: name: "scala" kind: "core" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusSecurityIdentity.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusSecurityIdentity.java index 54638f3d978f28..50bd92790f5b3f 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusSecurityIdentity.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusSecurityIdentity.java @@ -22,6 +22,7 @@ public class QuarkusSecurityIdentity implements SecurityIdentity { private final Set credentials; private final Map attributes; private final List>> permissionCheckers; + private final boolean anonymous; private QuarkusSecurityIdentity(Builder builder) { this.principal = builder.principal; @@ -29,6 +30,7 @@ private QuarkusSecurityIdentity(Builder builder) { this.credentials = Collections.unmodifiableSet(builder.credentials); this.attributes = Collections.unmodifiableMap(builder.attributes); this.permissionCheckers = Collections.unmodifiableList(builder.permissionCheckers); + this.anonymous = builder.anonymous; } @Override @@ -38,7 +40,7 @@ public Principal getPrincipal() { @Override public boolean isAnonymous() { - return false; + return anonymous; } @Override @@ -121,11 +123,13 @@ public static Builder builder() { } public static Builder builder(SecurityIdentity identity) { - return new Builder() + Builder builder = new Builder() .addAttributes(identity.getAttributes()) .addCredentials(identity.getCredentials()) .addRoles(identity.getRoles()) - .setPrincipal(identity.getPrincipal()); + .setPrincipal(identity.getPrincipal()) + .setAnonymous(identity.isAnonymous()); + return builder; } public static class Builder { @@ -135,6 +139,7 @@ public static class Builder { Set credentials = new HashSet<>(); Map attributes = new HashMap<>(); List>> permissionCheckers = new ArrayList<>(); + private boolean anonymous; boolean built = false; public Builder setPrincipal(Principal principal) { @@ -211,7 +216,25 @@ public Builder addPermissionChecker(Function> function) return this; } + /** + * Sets an anonymous identity status. + * + * @param anonymous the anonymous status + * @return This builder + */ + public Builder setAnonymous(boolean anonymous) { + if (built) { + throw new IllegalStateException(); + } + this.anonymous = anonymous; + return this; + } + public QuarkusSecurityIdentity build() { + if (principal == null && !anonymous) { + throw new IllegalStateException("Principal is null but anonymous status is false"); + } + built = true; return new QuarkusSecurityIdentity(this); } diff --git a/extensions/security/runtime/src/test/java/io/quarkus/security/runtime/QuarkusSecurityIdentityTest.java b/extensions/security/runtime/src/test/java/io/quarkus/security/runtime/QuarkusSecurityIdentityTest.java index c1704c16b5ef79..7d9dc11e67be38 100644 --- a/extensions/security/runtime/src/test/java/io/quarkus/security/runtime/QuarkusSecurityIdentityTest.java +++ b/extensions/security/runtime/src/test/java/io/quarkus/security/runtime/QuarkusSecurityIdentityTest.java @@ -1,11 +1,23 @@ package io.quarkus.security.runtime; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.security.Permission; +import java.security.Principal; +import java.util.Collections; +import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; +import io.quarkus.security.credential.Credential; import io.quarkus.security.credential.PasswordCredential; import io.quarkus.security.identity.SecurityIdentity; +import io.smallrye.mutiny.Uni; public class QuarkusSecurityIdentityTest { @@ -18,11 +30,132 @@ public void testCopyIdentity() throws Exception { .addAttribute("key", "value") .build(); + assertFalse(identity1.isAnonymous()); + SecurityIdentity identity2 = QuarkusSecurityIdentity.builder(identity1).build(); + assertFalse(identity1.isAnonymous()); assertEquals(identity1.getAttributes(), identity2.getAttributes()); assertEquals(identity1.getPrincipal(), identity2.getPrincipal()); assertEquals(identity1.getCredentials(), identity2.getCredentials()); assertEquals(identity1.getRoles(), identity2.getRoles()); } + + @Test + public void testAnonymousPrincipalWithCustomIdentity() throws Exception { + SecurityIdentity identity1 = new TestSecurityIdentityAnonymousPrincipal(); + assertTrue(identity1.isAnonymous()); + assertEquals("anonymous-principal", identity1.getPrincipal().getName()); + + SecurityIdentity identity2 = QuarkusSecurityIdentity.builder(identity1).build(); + assertTrue(identity2.isAnonymous()); + assertEquals("anonymous-principal", identity2.getPrincipal().getName()); + } + + @Test + public void testPrincipalNullAnonymousFalseWithBuilder() throws Exception { + QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder() + .addRole("admin") + .addCredential(new PasswordCredential("password".toCharArray())) + .addAttribute("key", "value"); + ; + + assertThrows(IllegalStateException.class, () -> builder.build()); + } + + @Test + public void testPrincipalNullAnonymousFalseWithCustomIdentity() throws Exception { + SecurityIdentity identity1 = new TestSecurityIdentityPrincipalNullAnonymousFalse(); + assertFalse(identity1.isAnonymous()); + assertNull(identity1.getPrincipal()); + + assertThrows(IllegalStateException.class, () -> QuarkusSecurityIdentity.builder(identity1).build()); + } + + @Test + public void testPrincipalNullAnonymousFalseWithCustomIdentityFixed() throws Exception { + SecurityIdentity identity1 = new TestSecurityIdentityPrincipalNullAnonymousFalse(); + assertFalse(identity1.isAnonymous()); + assertNull(identity1.getPrincipal()); + + SecurityIdentity identity2 = QuarkusSecurityIdentity.builder(identity1).setAnonymous(true).build(); + assertTrue(identity2.isAnonymous()); + assertNull(identity2.getPrincipal()); + } + + static class TestSecurityIdentityAnonymousPrincipal extends AbstractTestSecurityIdentity { + + @Override + public Principal getPrincipal() { + return new Principal() { + @Override + public String getName() { + return "anonymous-principal"; + } + }; + } + + @Override + public boolean isAnonymous() { + return true; + } + + } + + static class TestSecurityIdentityPrincipalNullAnonymousFalse extends AbstractTestSecurityIdentity { + + @Override + public Principal getPrincipal() { + return null; + } + + @Override + public boolean isAnonymous() { + return false; + } + + } + + static abstract class AbstractTestSecurityIdentity implements SecurityIdentity { + + @Override + public Set getRoles() { + return Collections.emptySet(); + } + + @Override + public boolean hasRole(String role) { + // TODO Auto-generated method stub + return false; + } + + @Override + public T getCredential(Class credentialType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getCredentials() { + return Collections.emptySet(); + } + + @Override + public T getAttribute(String name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public Uni checkPermission(Permission permission) { + // TODO Auto-generated method stub + return null; + } + + } } diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java index 4eb7c535760b46..a30fca16928262 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java @@ -59,6 +59,7 @@ import io.smallrye.graphql.cdi.config.ConfigKey; import io.smallrye.graphql.cdi.config.GraphQLConfig; import io.smallrye.graphql.cdi.producer.GraphQLProducer; +import io.smallrye.graphql.cdi.producer.SmallRyeContextAccessorProxy; import io.smallrye.graphql.schema.Annotations; import io.smallrye.graphql.schema.SchemaBuilder; import io.smallrye.graphql.schema.model.Argument; @@ -135,6 +136,7 @@ void additionalBean(BuildProducer additionalBeanProduce additionalBeanProducer.produce(AdditionalBeanBuildItem.builder() .addBeanClass(GraphQLConfig.class) .addBeanClass(GraphQLProducer.class) + .addBeanClass(SmallRyeContextAccessorProxy.class) .setUnremovable().build()); } @@ -471,7 +473,8 @@ void getGraphqlUiFinalDestination( AppArtifact artifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, GRAPHQL_UI_WEBJAR_GROUP_ID, GRAPHQL_UI_WEBJAR_ARTIFACT_ID); if (launchMode.getLaunchMode().isDevOrTest()) { - Path tempPath = WebJarUtil.copyResourcesForDevOrTest(curateOutcomeBuildItem, launchMode, artifact, + Path tempPath = WebJarUtil.copyResourcesForDevOrTest(liveReloadBuildItem, curateOutcomeBuildItem, launchMode, + artifact, GRAPHQL_UI_WEBJAR_PREFIX); WebJarUtil.updateUrl(tempPath.resolve(FILE_TO_UPDATE), graphQLPath, LINE_TO_UPDATE, LINE_FORMAT); WebJarUtil.updateUrl(tempPath.resolve(FILE_TO_UPDATE), graphQLUiPath, diff --git a/extensions/smallrye-graphql/runtime/pom.xml b/extensions/smallrye-graphql/runtime/pom.xml index 2ddb6d7a45711f..75067910e37142 100644 --- a/extensions/smallrye-graphql/runtime/pom.xml +++ b/extensions/smallrye-graphql/runtime/pom.xml @@ -39,6 +39,10 @@ io.quarkus quarkus-smallrye-context-propagation + + org.eclipse.microprofile.metrics + microprofile-metrics-api + diff --git a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java index 1dc1fb1af1fd5e..bdd7de78ac0769 100644 --- a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java +++ b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java @@ -410,7 +410,8 @@ void registerUiExtension( HEALTH_UI_WEBJAR_ARTIFACT_ID); if (launchMode.getLaunchMode().isDevOrTest()) { - Path tempPath = WebJarUtil.copyResourcesForDevOrTest(curateOutcomeBuildItem, launchMode, artifact, + Path tempPath = WebJarUtil.copyResourcesForDevOrTest(liveReloadBuildItem, curateOutcomeBuildItem, launchMode, + artifact, HEALTH_UI_WEBJAR_PREFIX); updateApiUrl(tempPath.resolve(JS_FILE_TO_UPDATE), healthPath); updateApiUrl(tempPath.resolve(INDEX_FILE_TO_UPDATE), healthPath); diff --git a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java index 437b0d73948287..52c7b52114d217 100644 --- a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java +++ b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java @@ -15,6 +15,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -32,6 +33,7 @@ import io.quarkus.spring.data.rest.deployment.paging.PagingAndSortingMethodsImplementor; import io.quarkus.spring.data.rest.deployment.paging.PagingAndSortingPropertiesProvider; import io.quarkus.spring.data.rest.runtime.RestDataPanacheExceptionMapper; +import io.quarkus.spring.data.rest.runtime.jta.TransactionalUpdateExecutor; class SpringDataRestProcessor { @@ -57,6 +59,11 @@ ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); } + @BuildStep + AdditionalBeanBuildItem registerTransactionalExecutor() { + return AdditionalBeanBuildItem.unremovableOf(TransactionalUpdateExecutor.class); + } + @BuildStep void registerCrudRepositories(CombinedIndexBuildItem indexBuildItem, BuildProducer implementationsProducer, diff --git a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/jta/TransactionalExecutor.java b/extensions/spring-data-rest/runtime/src/main/java/io/quarkus/spring/data/rest/runtime/jta/TransactionalUpdateExecutor.java similarity index 54% rename from extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/jta/TransactionalExecutor.java rename to extensions/spring-data-rest/runtime/src/main/java/io/quarkus/spring/data/rest/runtime/jta/TransactionalUpdateExecutor.java index e5576c316a2edd..9cccba32dd1827 100644 --- a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/jta/TransactionalExecutor.java +++ b/extensions/spring-data-rest/runtime/src/main/java/io/quarkus/spring/data/rest/runtime/jta/TransactionalUpdateExecutor.java @@ -1,13 +1,16 @@ -package io.quarkus.rest.data.panache.runtime.jta; +package io.quarkus.spring.data.rest.runtime.jta; import java.util.function.Supplier; import javax.inject.Singleton; import javax.transaction.Transactional; +import io.quarkus.rest.data.panache.runtime.UpdateExecutor; + @Singleton -public class TransactionalExecutor { +public class TransactionalUpdateExecutor implements UpdateExecutor { + @Override @Transactional public T execute(Supplier supplier) { return supplier.get(); diff --git a/extensions/spring-web/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/spring-web/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 98bc5d3eab823e..974429c85bdcfd 100644 --- a/extensions/spring-web/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/spring-web/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -14,5 +14,5 @@ metadata: - "java" - "kotlin" - "scala" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java index c4e8f034e82425..19f302b68f3651 100644 --- a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java +++ b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java @@ -103,7 +103,8 @@ public void getSwaggerUiFinalDestination( SWAGGER_UI_WEBJAR_ARTIFACT_ID); if (launchMode.getLaunchMode().isDevOrTest()) { - Path tempPath = WebJarUtil.copyResourcesForDevOrTest(curateOutcomeBuildItem, launchMode, artifact, + Path tempPath = WebJarUtil.copyResourcesForDevOrTest(liveReloadBuildItem, curateOutcomeBuildItem, launchMode, + artifact, SWAGGER_UI_WEBJAR_PREFIX); // Update index.html WebJarUtil.updateFile(tempPath.resolve("index.html"), diff --git a/extensions/tika/deployment/pom.xml b/extensions/tika/deployment/pom.xml index cb4f191e8b36f9..d2a1bba4af6488 100644 --- a/extensions/tika/deployment/pom.xml +++ b/extensions/tika/deployment/pom.xml @@ -20,6 +20,11 @@ io.quarkus quarkus-arc-deployment + + + io.quarkus + quarkus-apache-httpclient-deployment + io.quarkus quarkus-tika diff --git a/extensions/tika/runtime/pom.xml b/extensions/tika/runtime/pom.xml index f844829b966c12..d13a98db12ef0b 100644 --- a/extensions/tika/runtime/pom.xml +++ b/extensions/tika/runtime/pom.xml @@ -20,6 +20,11 @@ io.quarkus quarkus-arc + + + io.quarkus + quarkus-apache-httpclient + org.apache.tika tika-parsers diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java index 439f467bb6161f..745067cd0e71d1 100644 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java @@ -4,6 +4,7 @@ import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toMap; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -14,6 +15,7 @@ import io.quarkus.arc.Arc; import io.quarkus.vault.VaultKVSecretEngine; +import io.smallrye.mutiny.infrastructure.Infrastructure; public class VaultConfigSource implements ConfigSource { @@ -58,6 +60,11 @@ private Map getSecretConfig() { return cacheEntry.getValue(); } + if (!Infrastructure.canCallerThreadBeBlocked()) { + // running in a non blocking thread, best effort to return cached values if any + return cacheEntry != null ? cacheEntry.getValue() : Collections.emptyMap(); + } + Map properties = new HashMap<>(); try { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java index 5053eeb97f4bbb..4211c7ce8aae73 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java @@ -35,11 +35,16 @@ public class HttpRemoteDevClient implements RemoteDevClient { private final String url; private final String password; private final long reconnectTimeoutMillis; + private final long retryIntervalMillis; + private final int retryMaxAttempts; - public HttpRemoteDevClient(String url, String password, Duration reconnectTimeout) { + public HttpRemoteDevClient(String url, String password, Duration reconnectTimeout, Duration retryInterval, + int retryMaxAttempts) { this.url = url.endsWith("/") ? url.substring(0, url.length() - 1) : url; this.password = password; this.reconnectTimeoutMillis = reconnectTimeout.toMillis(); + this.retryIntervalMillis = retryInterval.toMillis(); + this.retryMaxAttempts = retryMaxAttempts; } @Override @@ -232,12 +237,12 @@ public void run() { } catch (Throwable e) { errorCount++; log.error("Remote dev request failed", e); - if (errorCount == 10) { + if (errorCount == retryMaxAttempts) { log.error("Connection failed after 10 retries, exiting"); return; } try { - Thread.sleep(2000); + Thread.sleep(retryIntervalMillis); } catch (InterruptedException ex) { } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClientProvider.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClientProvider.java index 7aab647be5f86a..e2b297bbb7d341 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClientProvider.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClientProvider.java @@ -32,6 +32,6 @@ public Optional getClient() { "Live reload URL set but no password, remote dev requires a password, set quarkus.live-reload.password on both server and client"); } return Optional.of(new HttpRemoteDevClient(liveReloadConfig.url.get(), liveReloadConfig.password.get(), - liveReloadConfig.connectTimeout)); + liveReloadConfig.connectTimeout, liveReloadConfig.retryInterval, liveReloadConfig.retryMaxAttempts)); } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java index 8210a8f9d6ec36..661d26219d7de0 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java @@ -49,6 +49,7 @@ import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.builditem.LogHandlerBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; @@ -325,7 +326,8 @@ public void setupDevConsoleRoutes( NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, LaunchModeBuildItem launchModeBuildItem, ShutdownContextBuildItem shutdownContext, - BuildProducer routeBuildItemBuildProducer) throws IOException { + BuildProducer routeBuildItemBuildProducer, + LiveReloadBuildItem liveReloadBuildItem) throws IOException { if (launchModeBuildItem.getDevModeType().orElse(null) != DevModeType.LOCAL) { return; } @@ -334,7 +336,8 @@ public void setupDevConsoleRoutes( AppArtifact devConsoleResourcesArtifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, "io.quarkus", "quarkus-vertx-http-deployment"); - Path devConsoleStaticResourcesDeploymentPath = WebJarUtil.copyResourcesForDevOrTest(curateOutcomeBuildItem, + Path devConsoleStaticResourcesDeploymentPath = WebJarUtil.copyResourcesForDevOrTest(liveReloadBuildItem, + curateOutcomeBuildItem, launchModeBuildItem, devConsoleResourcesArtifact, STATIC_RESOURCES_PATH); @@ -801,7 +804,7 @@ private String commonPath(List paths) { String commonPath = ""; List dirs = new ArrayList<>(paths.size()); for (int i = 0; i < paths.size(); i++) { - dirs.add(i, paths.get(i).split(File.separator)); + dirs.add(i, paths.get(i).split(Pattern.quote(File.separator))); } for (int j = 0; j < dirs.get(0).length; j++) { String thisDir = dirs.get(0)[j]; // grab the next directory name in the first path diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/OpenIdeHandler.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/OpenIdeHandler.java index ca9612022dd9a6..20be633d2eac4c 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/OpenIdeHandler.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/OpenIdeHandler.java @@ -62,9 +62,15 @@ private void typicalProcessLaunch(RoutingContext routingContext, String classNam } private String toFileName(String className, String lang, String srcMainPath) { - // TODO: handler inner classes + String effectiveClassName = className; + int dollarIndex = className.indexOf("$"); + if (dollarIndex > -1) { + // in this case we are dealing with inner classes, so we need to get the name of the outer class + // in order to use for conversion to the file name + effectiveClassName = className.substring(0, dollarIndex); + } return srcMainPath + File.separator + lang + File.separator - + (className.replace('.', File.separatorChar) + "." + LANG_TO_EXT.get(lang)); + + (effectiveClassName.replace('.', File.separatorChar) + "." + LANG_TO_EXT.get(lang)); } diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js b/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js index 67cde3a3ddd323..d4245cb40e369d 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js +++ b/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js @@ -11,13 +11,9 @@ var streamingPath = myself.attr('data-streamingPath'); if (typeof streamingPath === "undefined" ) { var streamingPath = "/dev/logstream"; } -// See if we should show on start -var showOnStart = myself.attr('data-showOnStart'); -if (typeof showOnStart === "undefined" ) { - var showOnStart = false; -} var zoom = 0.90; +var panelHeight; var linespace = 1.00; var tabspace = 1; var increment = 0.05; @@ -34,9 +30,6 @@ var filter = ""; var localstoragekey = "quarkus_logging_manager_state"; $('document').ready(function () { - if(!showOnStart){ - hideLog(); - } loadSettings(); openSocket(); @@ -52,6 +45,7 @@ $('document').ready(function () { document.addEventListener("mouseup", function(){ document.removeEventListener("mousemove", resize, false); + saveSettings(); }, false); logstreamStopStartButton.addEventListener("click", stopStartEvent); @@ -96,6 +90,14 @@ function loadSettings(){ zoom = state.zoom; applyZoom(); + if(state.panelHeight !== null && typeof(state.panelHeight) !== 'undefined'){ + panelHeight = state.panelHeight; + showLog(panelHeight); + }else{ + hideLog(); + panelHeight = null; + } + linespace = state.linespace; applyLineSpacing(); @@ -133,8 +135,11 @@ function loadSettings(){ function saveSettings(){ // Running state + const panel = document.getElementById("logstreamFooter"); + var state = { "zoom": zoom, + "panelHeight": panelHeight, "linespace": linespace, "tabspace": tabspace, "logScrolling": logScrolling, @@ -165,26 +170,44 @@ function saveSettings(){ } function showLog(){ - $("#logstreamFooter").css("height", "33vh"); + if (panelHeight === null || panelHeight === 'undefined') { + panelHeight = "33vh"; + } + $("#logstreamFooter").css("height", panelHeight); $("#logstreamManager").show(); $("#logstreamViewLogButton").hide(); $("#logstreamHideLogButton").show(); var element = document.getElementById("logstreamManager"); element.scrollIntoView({block: "end"}); + + saveSettings(); } function hideLog(){ + panelHeight = null; $("#logstreamFooter").css("height", "unset"); $("#logstreamViewLogButton").show(); $("#logstreamHideLogButton").hide(); $("#logstreamManager").hide(); + + saveSettings() } function resize(e){ const dx = m_pos - e.y; m_pos = e.y; const panel = document.getElementById("logstreamFooter"); - panel.style.height = (parseInt(getComputedStyle(panel, '').height) + dx) + "px"; + + if(panel.style.height === "unset"){ + panelHeight = null; + }else{ + panelHeight = parseInt(getComputedStyle(panel, '').height) + dx; + panelHeight = "" + panelHeight; + if(!panelHeight.endsWith("vh") && !panelHeight.endsWith("px")){ + panelHeight = panelHeight + "px"; + } + panel.style.height = panelHeight; + } } function addControlCListener(){ diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/main.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/main.html index 88832d3a76bb2d..e449d367b70415 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/main.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/main.html @@ -132,7 +132,12 @@ console.error("Unable to determine file name of class: '" + className + "'"); return; } - var fileName = className.replaceAll(".", "/") + "." + langToExt[lang]; + var effectiveClassName = className; + var dollarIndex = className.indexOf("$"); + if (dollarIndex > -1) { + effectiveClassName = className.substring(0, dollarIndex); + } + var fileName = effectiveClassName.replaceAll(".", "/") + "." + langToExt[lang]; if (ideLinkType == "client") { var location = ideClientLinkFormat.replace("{0}", srcMainPath + "/" + lang + "/" + fileName); if (line) { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/shutdown/ShutdownTimeoutDefaultExecutorTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/shutdown/ShutdownTimeoutDefaultExecutorTest.java new file mode 100644 index 00000000000000..22c61f4dc8e37a --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/shutdown/ShutdownTimeoutDefaultExecutorTest.java @@ -0,0 +1,101 @@ +package io.quarkus.vertx.http.shutdown; + +import java.io.IOException; +import java.net.Socket; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Event; +import javax.enterprise.event.Observes; +import javax.enterprise.event.ObservesAsync; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.vertx.core.Handler; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +/** + * Tests that shutdown will wait for current requests to finish, up to the timeout specified. + * + * This test records the current time, then sends a request to an endpoint that will take 50s to finish. + * + * After undeploy we verify that less than 50s has elapsed, as the shutdown should have proceeded anyway once + * the timeout of 100ms was reached. + */ +public class ShutdownTimeoutDefaultExecutorTest { + + protected static final int HANDLER_WAIT_TIME = 50000; + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setAllowTestClassOutsideDeployment(true) + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(ShutdownTimeoutDefaultExecutorTest.class) + .addAsResource(new StringAsset( + "quarkus.shutdown.timeout=PT0.1S\nquarkus.thread-pool.shutdown-check-interval=PT0.2S"), + "application.properties"); + } + }) + .setAfterUndeployListener(new Runnable() { + @Override + public void run() { + try { + ShutdownTimer.socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + Assertions.assertTrue(System.currentTimeMillis() - ShutdownTimer.requestStarted < HANDLER_WAIT_TIME); + } + }); + + @TestHTTPResource + URL url; + + @Test + public void testShutdownBehaviour() throws Exception { + ShutdownTimer.requestStarted = System.currentTimeMillis(); + ShutdownTimer.socket = new Socket(url.getHost(), url.getPort()); + ShutdownTimer.socket.getOutputStream() + .write("GET /shutdown HTTP/1.1\r\nHost: localhost\r\n\r\n".getBytes(StandardCharsets.UTF_8)); + Thread.sleep(1000); + } + + @ApplicationScoped + public static class ShutdownHandler { + + public void setup(@Observes Router router, Event event) { + ShutdownHandler thisHandler = this; + router.get("/shutdown").handler(new Handler() { + @Override + public void handle(RoutingContext routingContext) { + event.fireAsync(thisHandler).thenRun(new Runnable() { + @Override + public void run() { + routingContext.response().end(); + } + }); + } + }); + } + + // This observer is executed on a thread from the default executor + void blockExecutor(@ObservesAsync ShutdownHandler ignoreMe) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(HANDLER_WAIT_TIME); + } + + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java index 172a24288d0ad8..6bca11e7fe1f11 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java @@ -1,5 +1,7 @@ package io.quarkus.vertx.http.runtime; +import java.time.Duration; + import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -68,4 +70,10 @@ public class HttpBuildTimeConfig { */ @ConfigItem(defaultValue = "true") public boolean redirectToNonApplicationRootPath; + + /** + * The REST Assured client timeout for testing. + */ + @ConfigItem(defaultValue = "30s") + public Duration testTimeout; } diff --git a/extensions/websockets/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/websockets/runtime/src/main/resources/META-INF/quarkus-extension.yaml index cda7e43db787a9..db48e5f6ecf9fd 100644 --- a/extensions/websockets/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/websockets/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -15,4 +15,4 @@ metadata: name: "undertow-websockets" kind: "example" languages: "java" - artifact: "io.quarkus:quarkus-descriptor-json" + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index 7951a88ff4ab0f..bbe2daa7991883 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -273,6 +273,8 @@ + + .cache eclipse-format.xml LF ${format.skip} @@ -283,6 +285,8 @@ impsort-maven-plugin 1.6.0 + + .cache true ${format.skip} @@ -324,7 +328,6 @@ true true true - true clean install diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AssignabilityCheck.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AssignabilityCheck.java new file mode 100644 index 00000000000000..5d5599203827c1 --- /dev/null +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AssignabilityCheck.java @@ -0,0 +1,51 @@ +package io.quarkus.arc.processor; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.CompositeIndex; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Type; + +final class AssignabilityCheck { + + private final ConcurrentMap> cache; + private final IndexView index; + + public AssignabilityCheck(IndexView beanArchiveIndex, IndexView applicationIndex) { + this.cache = new ConcurrentHashMap<>(); + this.index = applicationIndex != null ? CompositeIndex.create(beanArchiveIndex, applicationIndex) : beanArchiveIndex; + } + + boolean isAssignableFrom(Type type1, Type type2) { + // java.lang.Object is assignable from any type + if (type1.name().equals(DotNames.OBJECT)) { + return true; + } + // type1 is the same as type2 + if (type1.name().equals(type2.name())) { + return true; + } + // type1 is a superclass + return getAssignables(type1.name()).contains(type2.name()); + } + + Set getAssignables(DotName name) { + return cache.computeIfAbsent(name, this::findAssignables); + } + + private Set findAssignables(DotName name) { + Set assignables = new HashSet<>(); + for (ClassInfo subclass : index.getAllKnownSubclasses(name)) { + assignables.add(subclass.name()); + } + for (ClassInfo implementor : index.getAllKnownImplementors(name)) { + assignables.add(implementor.name()); + } + return assignables; + } + +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index c42aa1a1bb37a4..29682a90070bdb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -78,6 +78,7 @@ public class BeanDeployment { private final List observers; final BeanResolverImpl beanResolver; + private final AssignabilityCheck assignabilityCheck; private final InterceptorResolver interceptorResolver; @@ -180,6 +181,7 @@ public class BeanDeployment { this.beans = new CopyOnWriteArrayList<>(); this.observers = new CopyOnWriteArrayList<>(); + this.assignabilityCheck = new AssignabilityCheck(beanArchiveIndex, applicationIndex); this.beanResolver = new BeanResolverImpl(this); this.interceptorResolver = new InterceptorResolver(this); this.transformUnproxyableClasses = builder.transformUnproxyableClasses; @@ -417,6 +419,10 @@ public BeanResolver getBeanResolver() { return beanResolver; } + public AssignabilityCheck getAssignabilityCheck() { + return assignabilityCheck; + } + boolean hasApplicationIndex() { return applicationIndex != null; } @@ -1003,7 +1009,7 @@ private DisposerInfo findDisposer(BeanInfo declaringBean, AnnotationTarget annot hasQualifier = false; } } - if (hasQualifier && beanResolver.matches(beanType, disposer.getDisposedParameterType())) { + if (hasQualifier && beanResolver.matches(disposer.getDisposedParameterType(), beanType)) { found.add(disposer); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index a1e9908c466d6e..ec25103b0086f7 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -1612,22 +1612,24 @@ protected void implementGetIdentifier(BeanInfo bean, ClassCreator beanCreator) { protected void implementEquals(BeanInfo bean, ClassCreator beanCreator) { MethodCreator equals = beanCreator.getMethodCreator("equals", boolean.class, Object.class).setModifiers(ACC_PUBLIC); + final ResultHandle obj = equals.getMethodParam(0); // if (this == obj) { // return true; // } - equals.ifTrue(equals.invokeStaticMethod(MethodDescriptors.OBJECTS_REFERENCE_EQUALS, equals.getThis(), - equals.getMethodParam(0))).trueBranch().returnValue(equals.load(true)); + equals.ifReferencesEqual(equals.getThis(), obj) + .trueBranch().returnValue(equals.load(true)); + // if (obj == null) { // return false; // } - equals.ifNull(equals.getMethodParam(0)).trueBranch().returnValue(equals.load(false)); + equals.ifNull(obj).trueBranch().returnValue(equals.load(false)); // if (!(obj instanceof InjectableBean)) { // return false; // } - equals.ifFalse(equals.instanceOf(equals.getMethodParam(0), InjectableBean.class)).trueBranch() + equals.ifFalse(equals.instanceOf(obj, InjectableBean.class)).trueBranch() .returnValue(equals.load(false)); // return identifier.equals(((InjectableBean) obj).getIdentifier()); - ResultHandle injectableBean = equals.checkCast(equals.getMethodParam(0), InjectableBean.class); + ResultHandle injectableBean = equals.checkCast(obj, InjectableBean.class); ResultHandle otherIdentifier = equals.invokeInterfaceMethod(MethodDescriptors.GET_IDENTIFIER, injectableBean); equals.returnValue(equals.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, equals.load(bean.getIdentifier()), otherIdentifier)); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java index 90d60969b1d270..a085f58197861a 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java @@ -16,13 +16,9 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; import javax.enterprise.inject.AmbiguousResolutionException; import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; -import org.jboss.jandex.DotName; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; import org.jboss.jandex.TypeVariable; @@ -32,33 +28,10 @@ class BeanResolverImpl implements BeanResolver { private final BeanDeployment beanDeployment; - private final ConcurrentMap> assignableFromMap; - - private final Function> assignableFromMapFunction; - private final Map> resolved; BeanResolverImpl(BeanDeployment beanDeployment) { this.beanDeployment = beanDeployment; - this.assignableFromMap = new ConcurrentHashMap<>(); - this.assignableFromMapFunction = name -> { - Set assignables = new HashSet<>(); - for (ClassInfo subclass : beanDeployment.getBeanArchiveIndex().getAllKnownSubclasses(name)) { - assignables.add(subclass.name()); - } - for (ClassInfo implementor : beanDeployment.getBeanArchiveIndex().getAllKnownImplementors(name)) { - assignables.add(implementor.name()); - } - if (beanDeployment.hasApplicationIndex()) { - for (ClassInfo subclass : beanDeployment.getApplicationIndex().getAllKnownSubclasses(name)) { - assignables.add(subclass.name()); - } - for (ClassInfo implementor : beanDeployment.getApplicationIndex().getAllKnownImplementors(name)) { - assignables.add(implementor.name()); - } - } - return assignables; - }; this.resolved = new ConcurrentHashMap<>(); } @@ -248,7 +221,7 @@ boolean parametersMatch(WildcardType requiredParameter, TypeVariable beanParamet boolean parametersMatch(Type requiredParameter, TypeVariable beanParameter) { for (Type bound : getUppermostTypeVariableBounds(beanParameter)) { - if (!isAssignableFrom(bound, requiredParameter)) { + if (!beanDeployment.getAssignabilityCheck().isAssignableFrom(bound, requiredParameter)) { return false; } } @@ -271,7 +244,7 @@ boolean boundsMatch(List bounds, List stricterBounds) { stricterBounds = getUppermostBounds(stricterBounds); for (Type bound : bounds) { for (Type stricterBound : stricterBounds) { - if (!isAssignableFrom(bound, stricterBound)) { + if (!beanDeployment.getAssignabilityCheck().isAssignableFrom(bound, stricterBound)) { return false; } } @@ -279,19 +252,6 @@ boolean boundsMatch(List bounds, List stricterBounds) { return true; } - boolean isAssignableFrom(Type type1, Type type2) { - // java.lang.Object is assignable from any type - if (type1.name().equals(DotNames.OBJECT)) { - return true; - } - // type1 is the same as type2 - if (type1.name().equals(type2.name())) { - return true; - } - // type1 is a superclass - return assignableFromMap.computeIfAbsent(type1.name(), assignableFromMapFunction).contains(type2.name()); - } - boolean lowerBoundsOfWildcardMatch(Type parameter, WildcardType requiredParameter) { return lowerBoundsOfWildcardMatch(singletonList(parameter), requiredParameter); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DisposerInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DisposerInfo.java index 9712660d82827c..175bae52a50ea8 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DisposerInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DisposerInfo.java @@ -86,7 +86,7 @@ Type getDisposedParameterType() { MethodParameterInfo initDisposedParam(MethodInfo disposerMethod) { List disposedParams = new ArrayList<>(); for (AnnotationInstance annotation : disposerMethod.annotations()) { - if (Kind.METHOD_PARAMETER.equals(annotation.target().kind()) && annotation.name().equals(DotNames.DISPOSES)) { + if (Kind.METHOD_PARAMETER == annotation.target().kind() && annotation.name().equals(DotNames.DISPOSES)) { disposedParams.add(annotation.target().asMethodParameter()); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java index 6e2e68273bfb5b..dc84000db17782 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java @@ -79,12 +79,30 @@ public final class MethodDescriptors { public static final MethodDescriptor OBJECT_EQUALS = MethodDescriptor.ofMethod(Object.class, "equals", boolean.class, Object.class); + /** + * No longer used - will be deleted + * + * @deprecated + */ + @Deprecated public static final MethodDescriptor OBJECT_HASH_CODE = MethodDescriptor.ofMethod(Object.class, "hashCode", int.class); + /** + * No longer used - will be deleted + * + * @deprecated + */ + @Deprecated public static final MethodDescriptor OBJECT_TO_STRING = MethodDescriptor.ofMethod(Object.class, "toString", String.class); public static final MethodDescriptor OBJECT_CONSTRUCTOR = MethodDescriptor.ofConstructor(Object.class); + /** + * No longer used - will be deleted + * + * @deprecated + */ + @Deprecated public static final MethodDescriptor OBJECTS_REFERENCE_EQUALS = MethodDescriptor.ofMethod(Objects.class, "referenceEquals", boolean.class, Object.class, Object.class); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index d747ece6a63f7c..94fe82c64c05f4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -143,7 +143,8 @@ static Set addInterceptedMethodCandidates(BeanDeployment beanDeploym List classLevelBindings, Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses) { return addInterceptedMethodCandidates(beanDeployment, classInfo, candidates, classLevelBindings, - bytecodeTransformerConsumer, transformUnproxyableClasses, new SubclassSkipPredicate(), false); + bytecodeTransformerConsumer, transformUnproxyableClasses, + new SubclassSkipPredicate(beanDeployment.getAssignabilityCheck()::isAssignableFrom), false); } static Set addInterceptedMethodCandidates(BeanDeployment beanDeployment, ClassInfo classInfo, @@ -396,10 +397,15 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str */ static class SubclassSkipPredicate implements Predicate { + private final BiFunction assignableFromFun; private ClassInfo clazz; private List regularMethods; private Set bridgeMethods = new HashSet<>(); + public SubclassSkipPredicate(BiFunction assignableFromFun) { + this.assignableFromFun = assignableFromFun; + } + void startProcessing(ClassInfo clazz) { this.clazz = clazz; this.regularMethods = new ArrayList<>(); @@ -459,8 +465,7 @@ private boolean hasImplementation(MethodInfo bridge) { for (int i = 0; i < bridgeParams.size(); i++) { Type bridgeParam = bridgeParams.get(i); Type param = params.get(i); - if (param.name().equals(bridgeParam.name()) - || bridgeParam.name().equals(DotNames.OBJECT)) { + if (assignableFromFun.apply(bridgeParam, param)) { continue; } else { paramsNotMatching = true; @@ -477,8 +482,8 @@ private boolean hasImplementation(MethodInfo bridge) { // both cases are a match return true; } else { - // as a last resort, we simply check equality of return Type - return bridge.returnType().name().equals(declaredMethod.returnType().name()); + // as a last resort, we simply check assignability of the return type + return assignableFromFun.apply(bridge.returnType(), declaredMethod.returnType()); } } return true; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java index 298109e92dbf1d..b23b85a08b57bd 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverInfo.java @@ -1,5 +1,8 @@ package io.quarkus.arc.processor; +import static io.quarkus.arc.processor.Annotations.find; +import static io.quarkus.arc.processor.Annotations.getParameterAnnotations; + import io.quarkus.arc.processor.BuildExtension.BuildContext; import io.quarkus.arc.processor.ObserverTransformer.ObserverTransformation; import io.quarkus.arc.processor.ObserverTransformer.TransformationContext; @@ -36,9 +39,11 @@ public class ObserverInfo implements InjectionTargetInfo { static ObserverInfo create(BeanInfo declaringBean, MethodInfo observerMethod, Injection injection, boolean isAsync, List transformers, BuildContext buildContext, boolean jtaCapabilities) { MethodParameterInfo eventParameter = initEventParam(observerMethod, declaringBean.getDeployment()); - AnnotationInstance priorityAnnotation = observerMethod.annotation(DotNames.PRIORITY); + AnnotationInstance priorityAnnotation = find( + getParameterAnnotations(declaringBean.getDeployment(), observerMethod, eventParameter.position()), + DotNames.PRIORITY); Integer priority; - if (priorityAnnotation != null && priorityAnnotation.target().equals(eventParameter)) { + if (priorityAnnotation != null) { priority = priorityAnnotation.value().asInt(); } else { priority = ObserverMethod.DEFAULT_PRIORITY; @@ -285,10 +290,9 @@ static TransactionPhase initTransactionPhase(boolean isAsync, BeanDeployment bea static Set initQualifiers(BeanDeployment beanDeployment, MethodInfo observerMethod, MethodParameterInfo eventParameter) { Set qualifiers = new HashSet<>(); - for (AnnotationInstance annotation : beanDeployment.getAnnotations(observerMethod)) { - if (annotation.target().equals(eventParameter)) { - beanDeployment.extractQualifiers(annotation).forEach(qualifiers::add); - } + for (AnnotationInstance annotation : getParameterAnnotations(beanDeployment, observerMethod, + eventParameter.position())) { + beanDeployment.extractQualifiers(annotation).forEach(qualifiers::add); } return qualifiers; } diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/SubclassSkipPredicateTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/SubclassSkipPredicateTest.java new file mode 100644 index 00000000000000..c7b0cb88cd9391 --- /dev/null +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/SubclassSkipPredicateTest.java @@ -0,0 +1,80 @@ +package io.quarkus.arc.processor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.processor.Methods.SubclassSkipPredicate; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import javax.enterprise.context.ApplicationScoped; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.junit.jupiter.api.Test; + +public class SubclassSkipPredicateTest { + + @Test + public void testPredicate() throws IOException { + IndexView index = Basics.index(Base.class, Submarine.class, Long.class, Number.class); + AssignabilityCheck assignabilityCheck = new AssignabilityCheck(index, null); + SubclassSkipPredicate predicate = new SubclassSkipPredicate(assignabilityCheck::isAssignableFrom); + + ClassInfo submarineClass = index.getClassByName(DotName.createSimple(Submarine.class.getName())); + predicate.startProcessing(submarineClass); + + List echos = submarineClass.methods().stream().filter(m -> m.name().equals("echo")) + .collect(Collectors.toList()); + assertEquals(2, echos.size()); + assertPredicate(echos, predicate); + + List getNames = submarineClass.methods().stream().filter(m -> m.name().equals("getName")) + .collect(Collectors.toList()); + assertEquals(2, getNames.size()); + assertPredicate(getNames, predicate); + + predicate.methodsProcessed(); + } + + private void assertPredicate(List methods, SubclassSkipPredicate predicate) { + for (MethodInfo method : methods) { + if (Methods.isBridge(method)) { + // Bridge method with impl + assertTrue(predicate.test(method)); + } else { + assertFalse(predicate.test(method)); + } + } + } + + static class Base { + + String echo(T payload) { + return payload.toString().toUpperCase(); + } + + T getName() { + return null; + } + + } + + @ApplicationScoped + static class Submarine extends Base { + + @Override + String echo(Long payload) { + return payload.toString(); + } + + @Override + Long getName() { + return 10l; + } + + } + +} diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Objects.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Objects.java index 9f45b93f56c62b..943e4c12880399 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Objects.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Objects.java @@ -1,5 +1,9 @@ package io.quarkus.arc.impl; +/** + * @deprecated will be removed. Use Gizmo's ifReferencesEqual instead. + */ +@Deprecated public final class Objects { private Objects() { diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bridge/BridgeMethodInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bridge/BridgeMethodInterceptionTest.java index a36f16860f924f..d9c9747894b4ad 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bridge/BridgeMethodInterceptionTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bridge/BridgeMethodInterceptionTest.java @@ -17,7 +17,7 @@ public class BridgeMethodInterceptionTest { @RegisterExtension public ArcTestContainer container = new ArcTestContainer(Base.class, Submarine.class, Ubot.class, Ponorka.class, Counter.class, Simple.class, SimpleInterceptor.class, ExampleApi.class, ExampleResource.class, - AbstractResource.class); + AbstractResource.class, NextBase.class, NextSubmarine.class); @Test public void testInterception() { @@ -54,6 +54,17 @@ public void testInterception() { assertEquals("TRUE", basePonorka.echo(true)); assertNull(basePonorka.getName()); assertEquals(4, counter.get()); + + counter.reset(); + NextSubmarine nextSubmarine = container.instance(NextSubmarine.class).get(); + assertEquals("foo", nextSubmarine.echo("foo")); + assertEquals(NextSubmarine.class.getSimpleName(), nextSubmarine.getName()); + assertEquals(2, counter.get()); + // Now let's invoke the bridge method... + NextBase nextBase = nextSubmarine; + assertEquals("foo", nextBase.echo("foo")); + assertEquals(NextSubmarine.class.getSimpleName(), nextBase.getName()); + assertEquals(4, counter.get()); } @Test @@ -123,4 +134,33 @@ static class Ponorka extends Base { } + static class NextBase> { + + String echo(T payload) { + return payload.toString().toUpperCase(); + } + + T getName() { + return null; + } + + } + + @ApplicationScoped + static class NextSubmarine extends NextBase { + + @Simple + @Override + String echo(String payload) { + return payload.toString(); + } + + @Simple + @Override + String getName() { + return NextSubmarine.class.getSimpleName(); + } + + } + } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerWithWildcardTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerWithWildcardTest.java new file mode 100644 index 00000000000000..4ce0e7226ebc30 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerWithWildcardTest.java @@ -0,0 +1,65 @@ +package io.quarkus.arc.test.producer.disposer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.test.ArcTestContainer; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.enterprise.util.TypeLiteral; +import javax.inject.Singleton; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class DisposerWithWildcardTest { + + @RegisterExtension + ArcTestContainer container = new ArcTestContainer(Producers.class); + + @Test + public void testDisposers() { + ArcContainer container = Arc.container(); + InstanceHandle> instanceA = container.instance(new TypeLiteral>() { + }); + assertTrue(instanceA.get().containsKey("A")); + instanceA.destroy(); + + InstanceHandle> instanceB = container.instance(new TypeLiteral>() { + }); + assertTrue(instanceB.get().containsKey("B")); + instanceB.destroy(); + + assertEquals(2, Producers.KEYS.size()); + } + + @Singleton + static class Producers { + + static final List KEYS = new CopyOnWriteArrayList<>(); + + @Singleton + @Produces + Map produceA() { + return Collections.singletonMap("A", 1l); + } + + @Singleton + @Produces + Map produceB() { + return Collections.singletonMap("B", 1); + } + + void dispose(@Disposes Map myMap) { + KEYS.addAll(myMap.keySet()); + } + + } + +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java index 7b106025ebb866..76416a48b0016c 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java @@ -10,6 +10,8 @@ public interface BootstrapConstants { String SERIALIZED_APP_MODEL = "quarkus-internal.serialized-app-model.path"; String DESCRIPTOR_FILE_NAME = "quarkus-extension.properties"; + String CONDITIONAL_DEPENDENCIES = "conditional-dependencies"; + String DEPENDENCY_CONDITION = "dependency-condition"; /** * Constant for sharing the additional mappings between test-sources and the corresponding application-sources. diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/CollectDependenciesBase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/CollectDependenciesBase.java index eeb73815d29e78..ffebc06fd81450 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/CollectDependenciesBase.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/CollectDependenciesBase.java @@ -6,6 +6,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,7 +44,7 @@ public void testCollectedDependencies() throws Exception { expected.addAll(deploymentDeps); } final List resolvedDeps = getTestResolver().resolveModel(root.toAppArtifact()).getFullDeploymentDeps(); - assertEquals(expected, resolvedDeps); + assertEquals(new HashSet<>(expected), new HashSet<>(resolvedDeps)); } protected BootstrapAppModelResolver getTestResolver() throws Exception { @@ -79,8 +80,9 @@ protected TsArtifact install(TsArtifact dep, Path p, String collectedInScope, bo return dep; } - protected void install(TsQuarkusExt ext) { + protected TsQuarkusExt install(TsQuarkusExt ext) { install(ext, true); + return ext; } protected void install(TsQuarkusExt ext, boolean collected) { @@ -150,6 +152,11 @@ protected void addCollectedDeploymentDep(TsArtifact ext) { deploymentDeps.add(new AppDependency(ext.toAppArtifact(), "compile", false)); } + protected void addManagedDep(TsQuarkusExt ext) { + addManagedDep(ext.runtime); + addManagedDep(ext.deployment); + } + protected void addManagedDep(TsArtifact dep) { root.addManagedDependency(new TsDependency(dep)); } diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java index ca5ed97db15397..3a6da4b9fb35d7 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java @@ -69,8 +69,9 @@ protected TsJar newJar() throws IOException { return new TsJar(workDir.resolve(UUID.randomUUID().toString())); } - protected void install(TsQuarkusExt extension) { + protected TsQuarkusExt install(TsQuarkusExt extension) { extension.install(repo); + return extension; } protected TsArtifact install(TsArtifact artifact) { diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsArtifact.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsArtifact.java index e65a602d63c7d4..041d37ea024cb6 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsArtifact.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsArtifact.java @@ -1,6 +1,7 @@ package io.quarkus.bootstrap.resolver; import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppArtifactKey; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -64,6 +65,8 @@ public interface ContentProvider { protected Properties pomProps; protected List pomProfiles = Collections.emptyList(); + private boolean installed; + public TsArtifact(String artifactId) { this(artifactId, DEFAULT_VERSION); } @@ -84,6 +87,10 @@ public TsArtifact(String groupId, String artifactId, String classifier, String t this.version = version; } + public AppArtifactKey getKey() { + return new AppArtifactKey(groupId, artifactId); + } + public String getGroupId() { return groupId; } @@ -226,6 +233,15 @@ public AppArtifact toAppArtifact() { * @param repoBuilder */ public void install(TsRepoBuilder repoBuilder) { + if (installed) { + return; + } + installed = true; + if (!extDeps.isEmpty()) { + for (TsQuarkusExt ext : extDeps) { + ext.install(repoBuilder); + } + } if (!deps.isEmpty()) { for (TsDependency dep : deps) { if (dep.artifact.getVersion() != null) { @@ -233,11 +249,6 @@ public void install(TsRepoBuilder repoBuilder) { } } } - if (!extDeps.isEmpty()) { - for (TsQuarkusExt ext : extDeps) { - ext.deployment.install(repoBuilder); - } - } try { repoBuilder.install(this, content == null ? null : content.getPath(repoBuilder.workDir)); } catch (IOException e) { diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java index 364f26efd137c1..15f1a342db6bfe 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java @@ -1,11 +1,17 @@ package io.quarkus.bootstrap.resolver; import io.quarkus.bootstrap.BootstrapConstants; +import java.util.ArrayList; +import java.util.List; public class TsQuarkusExt { protected final TsArtifact runtime; protected final TsArtifact deployment; + protected final List extDeps = new ArrayList<>(0); + protected final TsJar rtContent; + protected final PropsBuilder rtDescr = PropsBuilder.newInstance(); + private boolean installed; public TsQuarkusExt(String artifactId) { this(artifactId, TsArtifact.DEFAULT_VERSION); @@ -15,8 +21,34 @@ public TsQuarkusExt(String artifactId, String version) { runtime = TsArtifact.jar(artifactId, version); deployment = TsArtifact.jar(artifactId + "-deployment", version); deployment.addDependency(runtime); - runtime.setContent(new TsJar().addEntry(PropsBuilder.build(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT, deployment), - BootstrapConstants.DESCRIPTOR_PATH)); + rtContent = new TsJar(); + runtime.setContent(rtContent); + rtDescr.set(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT, deployment.toString()); + } + + public TsQuarkusExt setConditionalDeps(TsQuarkusExt... exts) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(exts[i++].getRuntime().toString()); + while (i < exts.length) { + buf.append(' ').append(exts[i++].getRuntime().toString()); + } + return setDescriptorProp(BootstrapConstants.CONDITIONAL_DEPENDENCIES, buf.toString()); + } + + public TsQuarkusExt setDependencyCondition(TsQuarkusExt... exts) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(exts[i++].getRuntime().getKey()); + while (i < exts.length) { + buf.append(' ').append(exts[i++].getRuntime().getKey()); + } + return setDescriptorProp(BootstrapConstants.DEPENDENCY_CONDITION, buf.toString()); + } + + public TsQuarkusExt setDescriptorProp(String name, String value) { + rtDescr.set(name, value); + return this; } public TsArtifact getRuntime() { @@ -27,14 +59,25 @@ public TsArtifact getDeployment() { return deployment; } - public TsQuarkusExt addDependency(TsQuarkusExt ext) { - runtime.addDependency(ext.runtime); + public TsQuarkusExt addDependency(TsQuarkusExt ext, TsArtifact... exclusions) { + extDeps.add(ext); + runtime.addDependency(ext.runtime, exclusions); deployment.addDependency(ext.deployment); return this; } public void install(TsRepoBuilder repo) { - repo.install(deployment); - repo.install(runtime); + if (installed) { + return; + } + installed = true; + rtContent.addEntry(rtDescr.build(), BootstrapConstants.DESCRIPTOR_PATH); + if (!extDeps.isEmpty()) { + for (TsQuarkusExt e : extDeps) { + e.install(repo); + } + } + deployment.install(repo); + runtime.install(repo); } } diff --git a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/AbstractTreeMojo.java b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/AbstractTreeMojo.java index 5971390b59e4f9..f1281757413d8b 100644 --- a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/AbstractTreeMojo.java +++ b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/AbstractTreeMojo.java @@ -2,20 +2,33 @@ import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; -import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; +import java.util.List; 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.Parameter; import org.apache.maven.project.MavenProject; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.repository.RemoteRepository; public class AbstractTreeMojo extends AbstractMojo { @Parameter(defaultValue = "${project}", readonly = true, required = true) protected MavenProject project; + @Component + RepositorySystem repoSystem; + + @Component + RemoteRepositoryManager remoteRepoManager; + + @Parameter(defaultValue = "${project.remoteRepositories}", readonly = true, required = true) + private List repos; + protected MavenArtifactResolver resolver; @Override @@ -34,7 +47,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { } protected MavenArtifactResolver resolver() throws BootstrapMavenException { - return resolver == null ? resolver = new MavenArtifactResolver(new BootstrapMavenContext()) : resolver; + return resolver == null + ? resolver = MavenArtifactResolver.builder() + .setRepositorySystem(repoSystem) + .setRemoteRepositoryManager(remoteRepoManager) + //.setRepositorySystemSession(repoSession) the session should be initialized with the loaded workspace + .setRemoteRepositories(repos) + .build() + : resolver; } protected void setupResolver(BootstrapAppModelResolver modelResolver) { diff --git a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/BuildTreeMojo.java b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/BuildTreeMojo.java index af95e9110cec18..19934d53b71438 100644 --- a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/BuildTreeMojo.java +++ b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/BuildTreeMojo.java @@ -9,7 +9,10 @@ /** * Displays Quarkus application build dependency tree including the deployment ones. + * + * @deprecated this mojo has moved to the quarkus-maven-plugin */ @Mojo(name = "build-tree", defaultPhase = LifecyclePhase.NONE, requiresDependencyResolution = ResolutionScope.NONE) +@Deprecated public class BuildTreeMojo extends AbstractTreeMojo { } diff --git a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java index 4a3024af6bac7f..0c1e01904a8b44 100644 --- a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java +++ b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java @@ -17,6 +17,7 @@ import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; +import io.quarkus.bootstrap.util.DependencyNodeUtils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -45,6 +46,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; +import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.DefaultArtifact; @@ -60,6 +62,7 @@ import org.eclipse.aether.resolution.ArtifactDescriptorResult; import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResult; +import org.eclipse.aether.util.artifact.JavaScopes; /** * Generates Quarkus extension descriptor for the runtime artifact. @@ -90,7 +93,7 @@ public class ExtensionDescriptorMojo extends AbstractMojo { RemoteRepositoryManager remoteRepoManager; @Component - BootstrapWorkspaceProvider workpaceProvider; + BootstrapWorkspaceProvider workspaceProvider; /** * The current repository/network configuration of Maven. @@ -167,6 +170,15 @@ public class ExtensionDescriptorMojo extends AbstractMojo { @Parameter(required = false, defaultValue = "${ignoreNotDetectedQuarkusCoreVersion") boolean ignoreNotDetectedQuarkusCoreVersion; + @Parameter + private List conditionalDependencies = new ArrayList<>(0); + + @Parameter + private List dependencyCondition = new ArrayList<>(0); + + @Parameter(property = "skipCodestartValidation") + boolean skipCodestartValidation; + AppArtifactCoords deploymentCoords; CollectResult collectedDeploymentDeps; @@ -179,8 +191,61 @@ public void execute() throws MojoExecutionException { validateExtensionDeps(); } + if (conditionalDependencies.isEmpty()) { + // if conditional dependencies haven't been configured + // we check whether there are direct optional dependencies on extensions + // that are configured with a dependency condition + // such dependencies will be registered as conditional + StringBuilder buf = null; + for (org.apache.maven.model.Dependency d : project.getDependencies()) { + if (!d.isOptional()) { + continue; + } + if (!d.getScope().isEmpty() + && !(d.getScope().equals(JavaScopes.COMPILE) || d.getScope().equals(JavaScopes.RUNTIME))) { + continue; + } + final Properties props = getExtensionDescriptor( + new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()), + false); + if (props == null || !props.containsKey(BootstrapConstants.DEPENDENCY_CONDITION)) { + continue; + } + if (buf == null) { + buf = new StringBuilder(); + } else { + buf.setLength(0); + } + buf.append(d.getGroupId()).append(':').append(d.getArtifactId()).append(':'); + if (d.getClassifier() != null) { + buf.append(d.getClassifier()); + } + buf.append(':').append(d.getType()).append(':').append(d.getVersion()); + conditionalDependencies.add(buf.toString()); + } + } + final Properties props = new Properties(); props.setProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT, deployment); + if (!conditionalDependencies.isEmpty()) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(AppArtifactCoords.fromString(conditionalDependencies.get(i++)).toString()); + while (i < conditionalDependencies.size()) { + buf.append(' ').append(AppArtifactCoords.fromString(conditionalDependencies.get(i++)).toString()); + } + props.setProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES, buf.toString()); + } + if (!dependencyCondition.isEmpty()) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(AppArtifactKey.fromString(dependencyCondition.get(i++)).toString()); + while (i < dependencyCondition.size()) { + buf.append(' ').append(AppArtifactKey.fromString(dependencyCondition.get(i++)).toString()); + } + props.setProperty(BootstrapConstants.DEPENDENCY_CONDITION, buf.toString()); + + } final Path output = outputDirectory.toPath().resolve(BootstrapConstants.META_INF); if (parentFirstArtifacts != null && !parentFirstArtifacts.isEmpty()) { @@ -291,6 +356,8 @@ public void execute() throws MojoExecutionException { setBuiltWithQuarkusCoreVersion(mapper, extObject); + completeCodestartArtifact(mapper, extObject); + final DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); @@ -303,9 +370,66 @@ public void execute() throws MojoExecutionException { } } + private void completeCodestartArtifact(ObjectMapper mapper, ObjectNode extObject) throws MojoExecutionException { + JsonNode mvalue = getJsonElement(extObject, METADATA, "codestart"); + if (mvalue == null || !mvalue.isObject()) { + return; + } + final ObjectNode codestartObject = (ObjectNode) mvalue; + mvalue = mvalue.get("artifact"); + if (mvalue == null) { + if (!skipCodestartValidation) { + throw new MojoExecutionException("Codestart artifact is missing from the " + extensionFile); + } + return; + } + org.eclipse.aether.artifact.Artifact codestartArtifact = DependencyNodeUtils.toArtifact(mvalue.asText()); + + if (codestartArtifact.getVersion() == null || codestartArtifact.getVersion().isEmpty()) { + codestartArtifact = codestartArtifact.setVersion(project.getVersion()); + codestartObject.put("artifact", + codestartArtifact.getGroupId() + ":" + codestartArtifact.getArtifactId() + ":" + + codestartArtifact.getClassifier() + ":" + codestartArtifact.getExtension() + ":" + + codestartArtifact.getVersion()); + } + if (!skipCodestartValidation) { + // first we look for it in the workspace, if it's in there we don't need to actually resolve the artifact, because it might not have been built yet + if (workspaceProvider.workspace().getProject(codestartArtifact.getGroupId(), + codestartArtifact.getArtifactId()) == null) { + try { + resolve(codestartArtifact); + } catch (MojoExecutionException e) { + throw new MojoExecutionException("Failed to resolve codestart artifact " + codestartArtifact, e); + } + } + } + } + + private static JsonNode getJsonElement(ObjectNode extObject, String... elements) { + JsonNode mvalue = extObject.get(elements[0]); + int i = 1; + while (i < elements.length) { + if (mvalue == null || !mvalue.isObject()) { + return null; + } + final String element = elements[i++]; + extObject = (ObjectNode) mvalue; + mvalue = extObject.get(element); + } + return mvalue; + } + private void setBuiltWithQuarkusCoreVersion(ObjectMapper mapper, ObjectNode extObject) throws MojoExecutionException { final QuarkusCoreDeploymentVersionLocator coreVersionLocator = new QuarkusCoreDeploymentVersionLocator(); - collectDeploymentDeps().getRoot().accept(coreVersionLocator); + final DependencyNode root; + try { + root = repoSystem.collectDependencies(repoSession, newCollectRuntimeDepsRequest()).getRoot(); + } catch (MojoExecutionException e) { + throw e; + } catch (Exception e) { + throw new MojoExecutionException("Failed to collect runtime dependencies of " + project.getArtifact(), e); + } + root.accept(coreVersionLocator); if (coreVersionLocator.coreVersion != null) { ObjectNode metadata; JsonNode mvalue = extObject.get(METADATA); @@ -337,12 +461,7 @@ private void validateExtensionDeps() throws MojoExecutionException { try { resolvedDeps = repoSystem.resolveDependencies(repoSession, - new DependencyRequest() - .setCollectRequest(newCollectRequest(new DefaultArtifact(project.getArtifact().getGroupId(), - project.getArtifact().getArtifactId(), - project.getArtifact().getClassifier(), - project.getArtifact().getArtifactHandler().getExtension(), - project.getArtifact().getVersion())))); + new DependencyRequest().setCollectRequest(newCollectRuntimeDepsRequest())); } catch (Exception e) { throw new MojoExecutionException("Failed to resolve dependencies of " + project.getArtifact(), e); } @@ -539,6 +658,26 @@ private void visitRuntimeDeps(RootNode root, Node currentNode, int currentId, De } private AppArtifactKey getDeploymentKey(org.eclipse.aether.artifact.Artifact a) throws MojoExecutionException { + final org.eclipse.aether.artifact.Artifact deployment = getDeploymentArtifact(a); + return deployment == null ? null : toKey(deployment); + } + + private org.eclipse.aether.artifact.Artifact getDeploymentArtifact(org.eclipse.aether.artifact.Artifact a) + throws MojoExecutionException { + final Properties props = getExtensionDescriptor(a, true); + if (props == null) { + return null; + } + final String deploymentStr = props.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT); + if (deploymentStr == null) { + throw new IllegalStateException("Quarkus extension runtime artifact " + a + " is missing " + + BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT + " property in its " + + BootstrapConstants.DESCRIPTOR_PATH); + } + return DependencyNodeUtils.toArtifact(deploymentStr); + } + + private Properties getExtensionDescriptor(org.eclipse.aether.artifact.Artifact a, boolean packaged) { final File f; try { f = resolve(a); @@ -547,31 +686,35 @@ private AppArtifactKey getDeploymentKey(org.eclipse.aether.artifact.Artifact a) return null; } // if it hasn't been packaged yet, we skip it, we are not packaging yet - if (isAnalyzable(f)) { - try (FileSystem fs = FileSystems.newFileSystem(f.toPath(), (ClassLoader) null)) { - final Path extDescr = fs.getPath(BootstrapConstants.DESCRIPTOR_PATH); - if (Files.exists(extDescr)) { - final Properties props = new Properties(); - try (BufferedReader reader = Files.newBufferedReader(extDescr)) { - props.load(reader); - } - final String deploymentStr = props.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT); - if (deploymentStr == null) { - throw new IllegalStateException("Quarkus extension runtime artifact " + a + " is missing " - + BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT + " property in its " - + BootstrapConstants.DESCRIPTOR_PATH); - } - return AppArtifactCoords.fromString(deploymentStr).getKey(); + if (packaged && !isJarFile(f)) { + return null; + } + try { + if (f.isDirectory()) { + return readExtensionDescriptor(f.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH)); + } else { + try (FileSystem fs = FileSystems.newFileSystem(f.toPath(), (ClassLoader) null)) { + return readExtensionDescriptor(fs.getPath(BootstrapConstants.DESCRIPTOR_PATH)); } - } catch (Throwable e) { - throw new IllegalStateException("Failed to read " + f, e); } + } catch (Throwable e) { + throw new IllegalStateException("Failed to read " + f, e); } - return null; + } + + private Properties readExtensionDescriptor(final Path extDescr) throws IOException { + if (!Files.exists(extDescr)) { + return null; + } + final Properties props = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(extDescr)) { + props.load(reader); + } + return props; } private static AppArtifactKey toKey(org.eclipse.aether.artifact.Artifact a) { - return new AppArtifactKey(a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getExtension()); + return DependencyNodeUtils.toKey(a); } private CollectResult collectDeploymentDeps() throws MojoExecutionException { @@ -593,6 +736,14 @@ private AppArtifactCoords getDeploymentCoords() { return deploymentCoords == null ? deploymentCoords = AppArtifactCoords.fromString(deployment) : deploymentCoords; } + private CollectRequest newCollectRuntimeDepsRequest() throws MojoExecutionException { + return newCollectRequest(new DefaultArtifact(project.getArtifact().getGroupId(), + project.getArtifact().getArtifactId(), + project.getArtifact().getClassifier(), + project.getArtifact().getArtifactHandler().getExtension(), + project.getArtifact().getVersion())); + } + private CollectRequest newCollectRequest(DefaultArtifact projectArtifact) throws MojoExecutionException { final ArtifactDescriptorResult projectDescr; try { @@ -618,7 +769,7 @@ private CollectRequest newCollectRequest(DefaultArtifact projectArtifact) throws return request; } - private boolean isAnalyzable(final File f) { + private boolean isJarFile(final File f) { return f != null && f.getName().endsWith(".jar") && f.exists() && !f.isDirectory(); } @@ -820,13 +971,15 @@ private static interface NodeHandler { private MavenArtifactResolver resolver() throws MojoExecutionException { if (resolver == null) { + final DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(repoSession); + session.setWorkspaceReader(workspaceProvider.workspace()); try { final BootstrapMavenContext ctx = new BootstrapMavenContext(BootstrapMavenContext.config() .setRepositorySystem(repoSystem) .setRemoteRepositoryManager(remoteRepoManager) - .setRepositorySystemSession(repoSession) + .setRepositorySystemSession(session) .setRemoteRepositories(repos) - .setCurrentProject(workpaceProvider.origin())); + .setCurrentProject(workspaceProvider.origin())); resolver = new MavenArtifactResolver(ctx); } catch (BootstrapMavenException e) { throw new MojoExecutionException("Failed to initialize Maven artifact resolver", e); diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java index 6e3d864336a411..28193461c84acf 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java @@ -181,32 +181,9 @@ private AppModel doResolveModel(AppArtifact appArtifact, List direct appArtifact.setPaths(PathsCollection.of(resolveResult.getArtifact().getFile().toPath())); } - final Set appDeps = new HashSet<>(); - final List userDeps = new ArrayList<>(); DependencyNode resolvedDeps = mvn.resolveManagedDependencies(mvnArtifact, directMvnDeps, managedDeps, managedRepos, excludedScopes.toArray(new String[0])).getRoot(); - final TreeDependencyVisitor visitor = new TreeDependencyVisitor(new DependencyVisitor() { - @Override - public boolean visitEnter(DependencyNode node) { - return true; - } - - @Override - public boolean visitLeave(DependencyNode node) { - final Dependency dep = node.getDependency(); - if (dep != null) { - final AppArtifact appArtifact = toAppArtifact(dep.getArtifact()); - appDeps.add(appArtifact.getKey()); - userDeps.add(new AppDependency(appArtifact, dep.getScope(), dep.isOptional())); - } - return true; - } - }); - for (DependencyNode child : resolvedDeps.getChildren()) { - child.accept(visitor); - } - ArtifactDescriptorResult appArtifactDescr = mvn.resolveDescriptor(toAetherArtifact(appArtifact)); if (managingProject == null) { managedDeps = appArtifactDescr.getManagedDependencies(); @@ -252,7 +229,9 @@ public boolean visitLeave(DependencyNode node) { } catch (RepositoryException e) { throw new AppModelResolverException("Failed to normalize the dependency graph", e); } - final BuildDependencyGraphVisitor buildDepsVisitor = new BuildDependencyGraphVisitor(appDeps, buildTreeConsumer); + final BuildDependencyGraphVisitor buildDepsVisitor = new BuildDependencyGraphVisitor( + deploymentInjector.allRuntimeDeps, + buildTreeConsumer); buildDepsVisitor.visit(resolvedDeps); final List requests = buildDepsVisitor.getArtifactRequests(); if (!requests.isEmpty()) { @@ -275,9 +254,6 @@ public boolean visitLeave(DependencyNode node) { collectPlatformProperties(appBuilder, managedDeps); - List fullDeploymentDeps = new ArrayList<>(userDeps.size() + deploymentDeps.size()); - fullDeploymentDeps.addAll(userDeps); - fullDeploymentDeps.addAll(deploymentDeps); //we need these to have a type of 'jar' //type is blank when loaded for (AppArtifactKey i : localProjects) { @@ -286,8 +262,7 @@ public boolean visitLeave(DependencyNode node) { return appBuilder .addDeploymentDeps(deploymentDeps) .setAppArtifact(appArtifact) - .addFullDeploymentDeps(fullDeploymentDeps) - .addRuntimeDeps(userDeps) + .addFullDeploymentDeps(deploymentDeps) .build(); } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersion.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersion.java new file mode 100644 index 00000000000000..01243b1b114572 --- /dev/null +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersion.java @@ -0,0 +1,359 @@ +package io.quarkus.bootstrap.resolver.maven; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import org.eclipse.aether.version.Version; + +public class BootstrapArtifactVersion implements Version { + + private final String version; + + private final Item[] items; + + private final int hash; + + /** + * Creates a generic version from the specified string. + * + * @param version The version string, must not be {@code null}. + */ + BootstrapArtifactVersion(String version) { + this.version = version; + items = parse(version); + hash = Arrays.hashCode(items); + } + + private static Item[] parse(String version) { + List items = new ArrayList<>(); + + for (Tokenizer tokenizer = new Tokenizer(version); tokenizer.next();) { + Item item = tokenizer.toItem(); + items.add(item); + } + + trimPadding(items); + + return items.toArray(new Item[items.size()]); + } + + private static void trimPadding(List items) { + Boolean number = null; + int end = items.size() - 1; + for (int i = end; i > 0; i--) { + Item item = items.get(i); + if (!Boolean.valueOf(item.isNumber()).equals(number)) { + end = i; + number = item.isNumber(); + } + if (end == i && (i == items.size() - 1 || items.get(i - 1).isNumber() == item.isNumber()) + && item.compareTo(null) == 0) { + items.remove(i); + end--; + } + } + } + + public int compareTo(Version obj) { + final Item[] these = items; + final Item[] those = ((BootstrapArtifactVersion) obj).items; + + boolean number = true; + + for (int index = 0;; index++) { + if (index >= these.length && index >= those.length) { + return 0; + } else if (index >= these.length) { + return -comparePadding(those, index, null); + } else if (index >= those.length) { + return comparePadding(these, index, null); + } + + Item thisItem = these[index]; + Item thatItem = those[index]; + + if (thisItem.isNumber() != thatItem.isNumber()) { + if (number == thisItem.isNumber()) { + return comparePadding(these, index, number); + } else { + return -comparePadding(those, index, number); + } + } else { + int rel = thisItem.compareTo(thatItem); + if (rel != 0) { + return rel; + } + number = thisItem.isNumber(); + } + } + } + + private static int comparePadding(Item[] items, int index, Boolean number) { + int rel = 0; + for (int i = index; i < items.length; i++) { + Item item = items[i]; + if (number != null && number != item.isNumber()) { + break; + } + rel = item.compareTo(null); + if (rel != 0) { + break; + } + } + return rel; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof BootstrapArtifactVersion) && compareTo((BootstrapArtifactVersion) obj) == 0; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return version; + } + + static final class Tokenizer { + + private static final Integer QUALIFIER_ALPHA = -5; + + private static final Integer QUALIFIER_BETA = -4; + + private static final Integer QUALIFIER_MILESTONE = -3; + + private static final Map QUALIFIERS; + + static { + QUALIFIERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + QUALIFIERS.put("alpha", QUALIFIER_ALPHA); + QUALIFIERS.put("beta", QUALIFIER_BETA); + QUALIFIERS.put("milestone", QUALIFIER_MILESTONE); + QUALIFIERS.put("cr", -2); + QUALIFIERS.put("rc", -2); + QUALIFIERS.put("snapshot", -1); + QUALIFIERS.put("ga", 0); + QUALIFIERS.put("final", 0); + QUALIFIERS.put("release", 0); + QUALIFIERS.put("", 0); + QUALIFIERS.put("sp", 1); + } + + private final String version; + + private int index; + + private String token; + + private boolean number; + + private boolean terminatedByNumber; + + Tokenizer(String version) { + this.version = (version.length() > 0) ? version : "0"; + } + + public boolean next() { + final int n = version.length(); + if (index >= n) { + return false; + } + + int state = -2; + + int start = index; + int end = n; + terminatedByNumber = false; + + for (; index < n; index++) { + char c = version.charAt(index); + + if (c == '.' || c == '-' || c == '_') { + end = index; + index++; + break; + } else { + int digit = Character.digit(c, 10); + if (digit >= 0) { + if (state == -1) { + end = index; + terminatedByNumber = true; + break; + } + if (state == 0) { + // normalize numbers and strip leading zeros (prereq for Integer/BigInteger handling) + start++; + } + state = (state > 0 || digit > 0) ? 1 : 0; + } else { + if (state >= 0) { + end = index; + break; + } + state = -1; + } + } + + } + + if (end - start > 0) { + token = version.substring(start, end); + number = state >= 0; + } else { + token = "0"; + number = true; + } + + return true; + } + + @Override + public String toString() { + return String.valueOf(token); + } + + public Item toItem() { + if (number) { + try { + if (token.length() < 10) { + return new Item(Item.KIND_INT, Integer.parseInt(token)); + } else { + return new Item(Item.KIND_BIGINT, new BigInteger(token)); + } + } catch (NumberFormatException e) { + throw new IllegalStateException(e); + } + } else { + if (index >= version.length()) { + if ("min".equalsIgnoreCase(token)) { + return Item.MIN; + } else if ("max".equalsIgnoreCase(token)) { + return Item.MAX; + } + } + if (terminatedByNumber && token.length() == 1) { + switch (token.charAt(0)) { + case 'a': + case 'A': + return new Item(Item.KIND_QUALIFIER, QUALIFIER_ALPHA); + case 'b': + case 'B': + return new Item(Item.KIND_QUALIFIER, QUALIFIER_BETA); + case 'm': + case 'M': + return new Item(Item.KIND_QUALIFIER, QUALIFIER_MILESTONE); + default: + } + } + Integer qualifier = QUALIFIERS.get(token); + if (qualifier != null) { + return new Item(Item.KIND_QUALIFIER, qualifier); + } else { + return new Item(Item.KIND_STRING, token.toLowerCase(Locale.ENGLISH)); + } + } + } + + } + + static final class Item { + + static final int KIND_MAX = 8; + + static final int KIND_BIGINT = 5; + + static final int KIND_INT = 4; + + static final int KIND_STRING = 3; + + static final int KIND_QUALIFIER = 2; + + static final int KIND_MIN = 0; + + static final Item MAX = new Item(KIND_MAX, "max"); + + static final Item MIN = new Item(KIND_MIN, "min"); + + private final int kind; + + private final Object value; + + Item(int kind, Object value) { + this.kind = kind; + this.value = value; + } + + public boolean isNumber() { + return (kind & KIND_QUALIFIER) == 0; // i.e. kind != string/qualifier + } + + public int compareTo(Item that) { + int rel; + if (that == null) { + // null in this context denotes the pad item (0 or "ga") + switch (kind) { + case KIND_MIN: + rel = -1; + break; + case KIND_MAX: + case KIND_BIGINT: + case KIND_STRING: + rel = 1; + break; + case KIND_INT: + case KIND_QUALIFIER: + rel = (Integer) value; + break; + default: + throw new IllegalStateException("unknown version item kind " + kind); + } + } else { + rel = kind - that.kind; + if (rel == 0) { + switch (kind) { + case KIND_MAX: + case KIND_MIN: + break; + case KIND_BIGINT: + rel = ((BigInteger) value).compareTo((BigInteger) that.value); + break; + case KIND_INT: + case KIND_QUALIFIER: + rel = ((Integer) value).compareTo((Integer) that.value); + break; + case KIND_STRING: + rel = ((String) value).compareToIgnoreCase((String) that.value); + break; + default: + throw new IllegalStateException("unknown version item kind " + kind); + } + } + } + return rel; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Item) && compareTo((Item) obj) == 0; + } + + @Override + public int hashCode() { + return value.hashCode() + kind * 31; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + } +} diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersionConstraint.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersionConstraint.java new file mode 100644 index 00000000000000..657033c6d31247 --- /dev/null +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapArtifactVersionConstraint.java @@ -0,0 +1,83 @@ +package io.quarkus.bootstrap.resolver.maven; + +import static java.util.Objects.requireNonNull; + +import java.util.Objects; +import org.eclipse.aether.version.Version; +import org.eclipse.aether.version.VersionConstraint; +import org.eclipse.aether.version.VersionRange; + +public class BootstrapArtifactVersionConstraint implements VersionConstraint { + + private final VersionRange range; + + private final Version version; + + /** + * Creates a version constraint from the specified version range. + * + * @param range The version range, must not be {@code null}. + */ + BootstrapArtifactVersionConstraint(VersionRange range) { + this.range = requireNonNull(range, "version range cannot be null"); + this.version = null; + } + + /** + * Creates a version constraint from the specified version. + * + * @param version The version, must not be {@code null}. + */ + BootstrapArtifactVersionConstraint(Version version) { + this.version = requireNonNull(version, "version cannot be null"); + this.range = null; + } + + public VersionRange getRange() { + return range; + } + + public Version getVersion() { + return version; + } + + public boolean containsVersion(Version version) { + if (range == null) { + return version.equals(this.version); + } else { + return range.containsVersion(version); + } + } + + @Override + public String toString() { + return String.valueOf((range == null) ? version : range); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } + + BootstrapArtifactVersionConstraint that = (BootstrapArtifactVersionConstraint) obj; + + return Objects.equals(range, that.range) && Objects.equals(version, that.getVersion()); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + hash(getRange()); + hash = hash * 31 + hash(getVersion()); + return hash; + } + + private static int hash(Object obj) { + return obj != null ? obj.hashCode() : 0; + } + +} diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java index baf7038db120db..247fe294b813ce 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java @@ -4,6 +4,7 @@ package io.quarkus.bootstrap.resolver.maven; import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.bootstrap.util.DependencyNodeUtils; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -15,14 +16,14 @@ public class BuildDependencyGraphVisitor { - private final Set appDeps; + private final Set allRuntimeDeps; private final StringBuilder buf; private final Consumer buildTreeConsumer; private final List depth; - private DependencyNode deploymentNode; - private DependencyNode runtimeNode; - private Artifact runtimeArtifact; + private DependencyNode currentDeployment; + private DependencyNode currentRuntime; + private Artifact runtimeArtifactToFind; /** * Nodes that are only present in the deployment class loader @@ -30,8 +31,8 @@ public class BuildDependencyGraphVisitor { private final List deploymentDepNodes = new ArrayList<>(); private final List requests = new ArrayList<>(); - public BuildDependencyGraphVisitor(Set appDeps, Consumer buildTreeConsumer) { - this.appDeps = appDeps; + public BuildDependencyGraphVisitor(Set allRuntimeDeps, Consumer buildTreeConsumer) { + this.allRuntimeDeps = allRuntimeDeps; this.buildTreeConsumer = buildTreeConsumer; if (buildTreeConsumer == null) { buf = null; @@ -52,35 +53,25 @@ public List getArtifactRequests() { public void visit(DependencyNode node) { if (depth != null) { - buf.setLength(0); - if (!depth.isEmpty()) { - for (int i = 0; i < depth.size() - 1; ++i) { - if (depth.get(i)) { - //buf.append("| "); - buf.append('\u2502').append(" "); - } else { - buf.append(" "); - } - } - if (depth.get(depth.size() - 1)) { - //buf.append("|- "); - buf.append('\u251c').append('\u2500').append(' '); - } else { - //buf.append("\\- "); - buf.append('\u2514').append('\u2500').append(' '); - } - } - buf.append(node.getArtifact()); - if (!depth.isEmpty()) { - buf.append(" (").append(node.getDependency().getScope()); - if (node.getDependency().isOptional()) { - buf.append(" optional"); - } - buf.append(')'); - } - buildTreeConsumer.accept(buf.toString()); + consume(node); + } + final Dependency dep = node.getDependency(); + + final DependencyNode previousDeployment = currentDeployment; + final DependencyNode previousRuntime = currentRuntime; + final Artifact previousRuntimeArtifact = runtimeArtifactToFind; + + final Artifact newRuntimeArtifact = DeploymentInjectingDependencyVisitor.getRuntimeArtifact(node); + if (newRuntimeArtifact != null) { + currentDeployment = node; + runtimeArtifactToFind = newRuntimeArtifact; + currentRuntime = null; + } else if (runtimeArtifactToFind != null && currentRuntime == null + && runtimeArtifactToFind.equals(dep.getArtifact())) { + currentRuntime = node; + runtimeArtifactToFind = null; } - visitEnter(node); + final List children = node.getChildren(); if (!children.isEmpty()) { final int childrenTotal = children.size(); @@ -94,13 +85,9 @@ public void visit(DependencyNode node) { depth.add(true); } int i = 0; - while (true) { + while (i < childrenTotal) { visit(children.get(i++)); - if (i < childrenTotal - 1) { - continue; - } else if (i == childrenTotal) { - break; - } else if (depth != null) { + if (depth != null && i == childrenTotal - 1) { depth.set(depth.size() - 1, false); } } @@ -110,18 +97,40 @@ public void visit(DependencyNode node) { } } visitLeave(node); + + currentDeployment = previousDeployment; + currentRuntime = previousRuntime; + runtimeArtifactToFind = previousRuntimeArtifact; } - private void visitEnter(DependencyNode node) { - final Dependency dep = node.getDependency(); - if (deploymentNode == null) { - runtimeArtifact = DeploymentInjectingDependencyVisitor.getRuntimeArtifact(node); - if (runtimeArtifact != null) { - deploymentNode = node; + private void consume(DependencyNode node) { + buf.setLength(0); + if (!depth.isEmpty()) { + for (int i = 0; i < depth.size() - 1; ++i) { + if (depth.get(i)) { + //buf.append("| "); + buf.append('\u2502').append(" "); + } else { + buf.append(" "); + } + } + if (depth.get(depth.size() - 1)) { + //buf.append("|- "); + buf.append('\u251c').append('\u2500').append(' '); + } else { + //buf.append("\\- "); + buf.append('\u2514').append('\u2500').append(' '); + } + } + buf.append(node.getArtifact()); + if (!depth.isEmpty()) { + buf.append(" (").append(node.getDependency().getScope()); + if (node.getDependency().isOptional()) { + buf.append(" optional"); } - } else if (runtimeArtifact != null && runtimeNode == null && runtimeArtifact.equals(dep.getArtifact())) { - runtimeNode = node; + buf.append(')'); } + buildTreeConsumer.accept(buf.toString()); } private void visitLeave(DependencyNode node) { @@ -133,16 +142,15 @@ private void visitLeave(DependencyNode node) { if (artifact.getFile() == null) { requests.add(new ArtifactRequest(node)); } - if (deploymentNode != null) { - if (runtimeNode == null && !appDeps.contains(new AppArtifactKey(artifact.getGroupId(), - artifact.getArtifactId(), artifact.getClassifier(), artifact.getExtension()))) { + if (currentDeployment != null) { + if (currentRuntime == null && !allRuntimeDeps.contains(DependencyNodeUtils.toKey(artifact))) { deploymentDepNodes.add(node); - } else if (runtimeNode == node) { - runtimeNode = null; - runtimeArtifact = null; + } else if (currentRuntime == node) { + currentRuntime = null; + runtimeArtifactToFind = null; } - if (deploymentNode == node) { - deploymentNode = null; + if (currentDeployment == node) { + currentDeployment = null; } } } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java index 500fa7823a7f07..96482533a04446 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java @@ -2,8 +2,13 @@ import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.BootstrapDependencyProcessingException; +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppModel; +import io.quarkus.bootstrap.model.PathsCollection; import io.quarkus.bootstrap.resolver.AppModelResolverException; +import io.quarkus.bootstrap.util.DependencyNodeUtils; import io.quarkus.bootstrap.util.ZipUtils; import java.io.BufferedReader; import java.io.File; @@ -11,19 +16,29 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Properties; +import java.util.Set; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.collection.DependencySelector; +import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.Exclusion; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.util.artifact.JavaScopes; +import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; import org.jboss.logging.Logger; /** @@ -34,8 +49,10 @@ public class DeploymentInjectingDependencyVisitor { private static final Logger log = Logger.getLogger(DeploymentInjectingDependencyVisitor.class); - static final String QUARKUS_RUNTIME_ARTIFACT = "quarkus.runtime"; - private static final String QUARKUS_DEPLOYMENT_ARTIFACT = "quarkus.deployment"; + private static final String QUARKUS_RUNTIME_ARTIFACT = "quarkus.runtime"; + private static final String QUARKUS_EXTENSION_DEPENDENCY = "quarkus.ext"; + + private static final Artifact[] NO_ARTIFACTS = new Artifact[0]; public static Artifact getRuntimeArtifact(DependencyNode dep) { return (Artifact) dep.getData().get(DeploymentInjectingDependencyVisitor.QUARKUS_RUNTIME_ARTIFACT); @@ -44,11 +61,16 @@ public static Artifact getRuntimeArtifact(DependencyNode dep) { private final MavenArtifactResolver resolver; private final List managedDeps; private final List mainRepos; + private final AppModel.Builder appBuilder; - private boolean collectExtensions = true; + private boolean collectingTopRuntimeNodes = true; + private final List topExtensionDeps = new ArrayList<>(); + private ExtensionDependency lastVisitedRuntimeExtNode; + private final Map allExtensions = new HashMap<>(); + private List conditionalDepsToProcess = new ArrayList<>(); + private final Deque> exclusionStack = new ArrayDeque<>(); - private List runtimeNodes = new ArrayList<>(); - private final AppModel.Builder appBuilder; + public final Set allRuntimeDeps = new HashSet<>(); public DeploymentInjectingDependencyVisitor(MavenArtifactResolver resolver, List managedDeps, List mainRepos, AppModel.Builder appBuilder) throws BootstrapDependencyProcessingException { @@ -75,101 +97,275 @@ public DeploymentInjectingDependencyVisitor(MavenArtifactResolver resolver, List } public boolean isInjectedDeps() { - return !runtimeNodes.isEmpty(); + return !topExtensionDeps.isEmpty(); } public void injectDeploymentDependencies(DependencyNode root) throws BootstrapDependencyProcessingException { - collectRuntimeExtensions(root.getChildren()); - // resolve and inject deployment dependencies - for (DependencyNode rtNode : runtimeNodes) { - replaceWith(rtNode, collectDependencies((Artifact) rtNode.getData().get(QUARKUS_DEPLOYMENT_ARTIFACT), - rtNode.getDependency().getExclusions())); + visitRuntimeDependencies(root.getChildren()); + + List activatedConditionalDeps = Collections.emptyList(); + + if (!conditionalDepsToProcess.isEmpty()) { + activatedConditionalDeps = new ArrayList<>(); + List unsatisfiedConditionalDeps = new ArrayList<>(); + while (!conditionalDepsToProcess.isEmpty()) { + final List tmp = unsatisfiedConditionalDeps; + unsatisfiedConditionalDeps = conditionalDepsToProcess; + conditionalDepsToProcess = tmp; + final int totalConditionsToProcess = unsatisfiedConditionalDeps.size(); + final Iterator i = unsatisfiedConditionalDeps.iterator(); + while (i.hasNext()) { + final ConditionalDependency cd = i.next(); + final boolean satisfied = cd.isSatisfied(); + if (!satisfied) { + continue; + } + i.remove(); + + cd.activate(); + activatedConditionalDeps.add(cd); + } + if (totalConditionsToProcess == unsatisfiedConditionalDeps.size()) { + // none of the dependencies was satisfied + break; + } + conditionalDepsToProcess.addAll(unsatisfiedConditionalDeps); + unsatisfiedConditionalDeps.clear(); + } + } + + // resolve and inject deployment dependency branches for the top (first met) runtime extension nodes + for (ExtensionDependency extDep : topExtensionDeps) { + injectDeploymentDependencies(extDep); } + + if (!activatedConditionalDeps.isEmpty()) { + for (ConditionalDependency cd : activatedConditionalDeps) { + injectDeploymentDependencies(cd.getExtensionDependency()); + } + } + } + + private boolean isRuntimeArtifact(AppArtifactKey key) { + return allRuntimeDeps.contains(key); } - private void collectRuntimeExtensions(List list) { + private void visitRuntimeDependencies(List list) { int i = 0; while (i < list.size()) { - collectRuntimeExtensions(list.get(i++)); + visitRuntimeDependency(list.get(i++)); } } - private void collectRuntimeExtensions(DependencyNode node) { - final Artifact artifact = node.getArtifact(); - if (!artifact.getExtension().equals("jar")) { - return; + private void visitRuntimeDependency(DependencyNode node) { + Artifact artifact = node.getArtifact(); + + if (allRuntimeDeps.add(DependencyNodeUtils.toKey(artifact))) { + artifact = resolve(artifact); + final AppArtifact appArtifact = toAppArtifact(artifact); + final AppDependency appDep = new AppDependency(appArtifact, node.getDependency().getScope(), + node.getDependency().isOptional()); + appBuilder.addRuntimeDep(appDep); + appBuilder.addFullDeploymentDep(appDep); + } + + final boolean prevCollectingTopRtNodes = collectingTopRuntimeNodes; + final ExtensionDependency prevLastVisitedRtExtNode = lastVisitedRuntimeExtNode; + + final boolean popExclusions; + if (popExclusions = !node.getDependency().getExclusions().isEmpty()) { + exclusionStack.addLast(node.getDependency().getExclusions()); } - final Path path = resolve(artifact); - final boolean parentCollectsExtensions = collectExtensions; + try { - if (Files.isDirectory(path)) { - collectExtensions &= !processMetaInfDir(node, path.resolve(BootstrapConstants.META_INF)); - } else { - try (FileSystem artifactFs = ZipUtils.newFileSystem(path)) { - collectExtensions &= !processMetaInfDir(node, artifactFs.getPath(BootstrapConstants.META_INF)); - } + final ExtensionDependency extDep = getExtensionDependencyOrNull(node, artifact); + if (extDep != null) { + extDep.info.ensureActivated(); + visitExtensionDependency(extDep); } - collectRuntimeExtensions(node.getChildren()); + visitRuntimeDependencies(node.getChildren()); } catch (DeploymentInjectionException e) { throw e; } catch (Exception t) { throw new DeploymentInjectionException("Failed to inject extension deployment dependencies", t); - } finally { - collectExtensions = parentCollectsExtensions; } + + if (popExclusions) { + exclusionStack.pollLast(); + } + collectingTopRuntimeNodes = prevCollectingTopRtNodes; + lastVisitedRuntimeExtNode = prevLastVisitedRtExtNode; } - /** - * @return true in case the node is a Quarkus runtime extension artifact, otherwise - false - * @throws BootstrapDependencyProcessingException in case of a failure - */ - private boolean processMetaInfDir(DependencyNode node, Path metaInfDir) throws BootstrapDependencyProcessingException { - final Path p = metaInfDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME); - return Files.exists(p) ? processPlatformArtifact(node, p) : false; + private ExtensionDependency getExtensionDependencyOrNull(DependencyNode node, Artifact artifact) + throws BootstrapDependencyProcessingException { + ExtensionDependency extDep = ExtensionDependency.get(node); + if (extDep != null) { + return extDep; + } + final ExtensionInfo extInfo = getExtensionInfoOrNull(artifact); + if (extInfo != null) { + Collection exclusions; + if (!exclusionStack.isEmpty()) { + if (exclusionStack.size() == 1) { + exclusions = exclusionStack.peekLast(); + } else { + exclusions = new ArrayList<>(); + for (Collection set : exclusionStack) { + exclusions.addAll(set); + } + } + } else { + exclusions = Collections.emptyList(); + } + return new ExtensionDependency(extInfo, node, exclusions); + } + return null; } - /** - * @return true in case the node is a Quarkus runtime extension artifact, otherwise - false - * @throws BootstrapDependencyProcessingException in case of a failure - */ - private boolean processPlatformArtifact(DependencyNode node, Path descriptor) + private void visitExtensionDependency(ExtensionDependency extDep) throws BootstrapDependencyProcessingException { - final Properties rtProps = resolveDescriptor(descriptor); - if (rtProps == null) { - return false; + + managedDeps.add(new Dependency(extDep.info.deploymentArtifact, JavaScopes.COMPILE)); + + collectConditionalDependencies(extDep); + + if (collectingTopRuntimeNodes) { + collectingTopRuntimeNodes = false; + topExtensionDeps.add(extDep); } - appBuilder.handleExtensionProperties(rtProps, node.getArtifact().toString()); - final String value = rtProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT); - if (value == null) { - return false; + if (lastVisitedRuntimeExtNode != null) { + lastVisitedRuntimeExtNode.addExtensionDependency(extDep); } - Artifact deploymentArtifact = toArtifact(value); - if (deploymentArtifact.getVersion() == null || deploymentArtifact.getVersion().isEmpty()) { - deploymentArtifact = deploymentArtifact.setVersion(node.getArtifact().getVersion()); + lastVisitedRuntimeExtNode = extDep; + } + + private void collectConditionalDependencies(ExtensionDependency dependent) + throws BootstrapDependencyProcessingException { + if (dependent.info.conditionalDeps.length == 0 || dependent.conditionalDepsQueued) { + return; } - managedDeps.add(new Dependency(deploymentArtifact, JavaScopes.COMPILE)); - if (collectExtensions) { - node.setData(QUARKUS_DEPLOYMENT_ARTIFACT, deploymentArtifact); - runtimeNodes.add(node); + dependent.conditionalDepsQueued = true; + + final DependencySelector selector = dependent.exclusions == null ? null + : new ExclusionDependencySelector(dependent.exclusions); + for (Artifact conditionalArtifact : dependent.info.conditionalDeps) { + if (selector != null && !selector.selectDependency(new Dependency(conditionalArtifact, "runtime"))) { + continue; + } + final ExtensionInfo conditionalInfo = getExtensionInfoOrNull(conditionalArtifact); + final ConditionalDependency conditionalDep = new ConditionalDependency(conditionalInfo, dependent); + conditionalDepsToProcess.add(conditionalDep); + collectConditionalDependencies(conditionalDep.getExtensionDependency()); } - return true; } - private void replaceWith(DependencyNode originalNode, DependencyNode newNode) + private ExtensionInfo getExtensionInfoOrNull(Artifact artifact) throws BootstrapDependencyProcessingException { + if (!artifact.getExtension().equals("jar")) { + return null; + } + final AppArtifactKey extKey = DependencyNodeUtils.toKey(artifact); + ExtensionInfo ext = allExtensions.get(extKey); + if (ext != null) { + return ext; + } + + artifact = resolve(artifact); + final Path path = artifact.getFile().toPath(); + if (Files.isDirectory(path)) { + ext = createExtensionInfoOrNull(artifact, path.resolve(BootstrapConstants.DESCRIPTOR_PATH)); + } else { + try (FileSystem artifactFs = ZipUtils.newFileSystem(path)) { + ext = createExtensionInfoOrNull(artifact, artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH)); + } catch (IOException e) { + throw new DeploymentInjectionException("Failed to read " + path, e); + } + } + allExtensions.put(extKey, ext); + return ext; + } + + private ExtensionInfo createExtensionInfoOrNull(Artifact artifact, Path descriptor) throws BootstrapDependencyProcessingException { - List children = newNode.getChildren(); - if (children.isEmpty()) { + final Properties rtProps = readDescriptor(descriptor); + if (rtProps == null) { + return null; + } + return new ExtensionInfo(artifact, rtProps); + } + + private void injectDeploymentDependencies(ExtensionDependency extDep) + throws BootstrapDependencyProcessingException { + log.debugf("Injecting deployment dependency %s", extDep.info.deploymentArtifact); + final DependencyNode deploymentNode = collectDependencies(extDep.info.deploymentArtifact, extDep.exclusions); + final List deploymentDeps = deploymentNode.getChildren(); + if (!replaceDirectDepBranch(extDep, deploymentDeps)) { throw new BootstrapDependencyProcessingException( - "No dependencies collected for Quarkus extension deployment artifact " + newNode.getArtifact() - + " while at least the corresponding runtime artifact " + originalNode.getArtifact() - + " is expected"); + "Quarkus extension deployment artifact " + deploymentNode.getArtifact() + + " does not appear to depend on the corresponding runtime artifact " + + extDep.info.runtimeArtifact); } - log.debugf("Injecting deployment dependency %s", newNode); - originalNode.setData(QUARKUS_RUNTIME_ARTIFACT, originalNode.getArtifact()); - originalNode.setArtifact(newNode.getArtifact()); - originalNode.getDependency().setArtifact(newNode.getArtifact()); - originalNode.setChildren(children); + final DependencyNode runtimeNode = extDep.runtimeNode; + runtimeNode.setData(QUARKUS_RUNTIME_ARTIFACT, runtimeNode.getArtifact()); + runtimeNode.setArtifact(deploymentNode.getArtifact()); + runtimeNode.getDependency().setArtifact(deploymentNode.getArtifact()); + runtimeNode.setChildren(deploymentDeps); + } + + private boolean replaceDirectDepBranch(ExtensionDependency extDep, List deploymentDeps) + throws BootstrapDependencyProcessingException { + int i = 0; + DependencyNode inserted = null; + while (i < deploymentDeps.size()) { + final Artifact a = deploymentDeps.get(i).getArtifact(); + if (a == null) { + continue; + } + if (isSameKey(extDep.info.runtimeArtifact, a)) { + // we are not comparing the version in the above condition because the runtime version + // may appear to be different then the deployment one and that's ok + // e.g. the version of the runtime artifact could be managed by a BOM + // but overridden by the user in the project config. The way the deployment deps + // are resolved here, the deployment version of the runtime artifact will be the one from the BOM. + inserted = new DefaultDependencyNode(extDep.runtimeNode); + inserted.setChildren(extDep.runtimeNode.getChildren()); + deploymentDeps.set(i, inserted); + break; + } + ++i; + } + if (inserted == null) { + return false; + } + + if (extDep.runtimeExtensionDeps != null) { + for (ExtensionDependency dep : extDep.runtimeExtensionDeps) { + for (DependencyNode deploymentDep : deploymentDeps) { + if (deploymentDep == inserted) { + continue; + } + if (replaceRuntimeBranch(dep, deploymentDep.getChildren())) { + break; + } + } + } + } + + return true; + } + + private boolean replaceRuntimeBranch(ExtensionDependency extNode, List deploymentNodes) + throws BootstrapDependencyProcessingException { + if (replaceDirectDepBranch(extNode, deploymentNodes)) { + return true; + } + for (DependencyNode deploymentNode : deploymentNodes) { + if (replaceRuntimeBranch(extNode, deploymentNode.getChildren())) { + return true; + } + } + return false; } private DependencyNode collectDependencies(Artifact artifact, Collection exclusions) @@ -187,25 +383,23 @@ private DependencyNode collectDependencies(Artifact artifact, Collection exclusions; + boolean conditionalDepsQueued; + private List runtimeExtensionDeps; + + ExtensionDependency(ExtensionInfo info, DependencyNode node, Collection exclusions) { + this.runtimeNode = node; + this.info = info; + this.exclusions = exclusions; + + @SuppressWarnings("unchecked") + final Map data = (Map) node.getData(); + if (data.isEmpty()) { + node.setData(QUARKUS_EXTENSION_DEPENDENCY, this); + } else if (data.put(QUARKUS_EXTENSION_DEPENDENCY, this) != null) { + throw new IllegalStateException( + "Dependency node " + node + " has already been associated with an extension dependency"); + } + } + + void addExtensionDependency(ExtensionDependency dep) { + if (runtimeExtensionDeps == null) { + runtimeExtensionDeps = new ArrayList<>(); + } + runtimeExtensionDeps.add(dep); + } + } + + private class ConditionalDependency { + + final ExtensionInfo info; + final ExtensionDependency dependent; + private ExtensionDependency dependency; + private boolean activated; + + private ConditionalDependency(ExtensionInfo info, ExtensionDependency dependent) { + this.info = Objects.requireNonNull(info, "Extension info is null"); + this.dependent = dependent; + } + + ExtensionDependency getExtensionDependency() { + if (dependency == null) { + final DefaultDependencyNode rtNode = new DefaultDependencyNode(new Dependency(info.runtimeArtifact, "runtime")); + rtNode.setVersion(new BootstrapArtifactVersion(info.runtimeArtifact.getVersion())); + rtNode.setVersionConstraint(new BootstrapArtifactVersionConstraint( + new BootstrapArtifactVersion(info.runtimeArtifact.getVersion()))); + dependency = new ExtensionDependency(info, rtNode, dependent.exclusions); + } + return dependency; + } + + void activate() throws BootstrapDependencyProcessingException { + if (activated) { + return; + } + activated = true; + collectingTopRuntimeNodes = false; + final ExtensionDependency extDep = getExtensionDependency(); + final DependencyNode originalNode = collectDependencies(info.runtimeArtifact, extDep.exclusions); + final DependencyNode rtNode = extDep.runtimeNode; + // if this node has conditional dependencies on its own, they may have been activated by this time + // in which case they would be included into its children + List currentChildren = rtNode.getChildren(); + if (currentChildren == null || currentChildren.isEmpty()) { + rtNode.setChildren(originalNode.getChildren()); + } else { + currentChildren.addAll(originalNode.getChildren()); + } + visitRuntimeDependency(rtNode); + dependent.runtimeNode.getChildren().add(rtNode); + } + + boolean isSatisfied() throws BootstrapDependencyProcessingException { + if (info.dependencyCondition == null) { + return true; + } + for (AppArtifactKey key : info.dependencyCondition) { + if (!isRuntimeArtifact(key)) { + return false; } } + return true; } - return new DefaultArtifact(groupId, artifactId, classifier, type, version); } - private static void illegalDependencyFormat(String str) { - throw new IllegalArgumentException("Bad artifact coordinates " + str - + ", expected format is :[:[:]]:"); + private static boolean isSameKey(Artifact a1, Artifact a2) { + return a2.getArtifactId().equals(a1.getArtifactId()) + && a2.getGroupId().equals(a1.getGroupId()) + && a2.getClassifier().equals(a1.getClassifier()) + && a2.getExtension().equals(a1.getExtension()); + } + + private static AppArtifact toAppArtifact(Artifact artifact) { + final AppArtifact appArtifact = new AppArtifact(artifact.getGroupId(), artifact.getArtifactId(), + artifact.getClassifier(), artifact.getExtension(), artifact.getVersion()); + final File file = artifact.getFile(); + if (file != null) { + appArtifact.setPaths(PathsCollection.of(file.toPath())); + } + return appArtifact; } } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/util/DependencyNodeUtils.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/util/DependencyNodeUtils.java new file mode 100644 index 00000000000000..cc62881d2e62bc --- /dev/null +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/util/DependencyNodeUtils.java @@ -0,0 +1,98 @@ +package io.quarkus.bootstrap.util; + +import io.quarkus.bootstrap.model.AppArtifactKey; +import java.io.PrintWriter; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.DependencyNode; + +public class DependencyNodeUtils { + + public static AppArtifactKey toKey(Artifact artifact) { + return new AppArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), + artifact.getClassifier(), artifact.getExtension()); + } + + public static Artifact toArtifact(String str) { + return toArtifact(str, 0); + } + + private static Artifact toArtifact(String str, int offset) { + String groupId = null; + String artifactId = null; + String classifier = ""; + String type = "jar"; + String version = null; + + int colon = str.indexOf(':', offset); + final int length = str.length(); + if (colon < offset + 1 || colon == length - 1) { + illegalDependencyFormat(str); + } + groupId = str.substring(offset, colon); + offset = colon + 1; + colon = str.indexOf(':', offset); + if (colon < 0) { + artifactId = str.substring(offset, length); + } else { + if (colon == length - 1) { + illegalDependencyFormat(str); + } + artifactId = str.substring(offset, colon); + offset = colon + 1; + colon = str.indexOf(':', offset); + if (colon < 0) { + version = str.substring(offset, length); + } else { + if (colon == length - 1) { + illegalDependencyFormat(str); + } + type = str.substring(offset, colon); + offset = colon + 1; + colon = str.indexOf(':', offset); + if (colon < 0) { + version = str.substring(offset, length); + } else { + if (colon == length - 1) { + illegalDependencyFormat(str); + } + classifier = type; + type = str.substring(offset, colon); + version = str.substring(colon + 1); + } + } + } + return new DefaultArtifact(groupId, artifactId, classifier, type, version); + } + + private static void illegalDependencyFormat(String str) { + throw new IllegalArgumentException("Bad artifact coordinates " + str + + ", expected format is :[:|[::]]:"); + } + + public static void printTree(DependencyNode node) { + PrintWriter out = new PrintWriter(System.out); + try { + printTree(node, out); + } finally { + out.flush(); + } + } + + public static void printTree(DependencyNode node, PrintWriter out) { + out.println("Dependency tree for " + node.getArtifact()); + printTree(node, 0, out); + } + + private static void printTree(DependencyNode node, int depth, PrintWriter out) { + if (node.getArtifact() != null) { + for (int i = 0; i < depth; ++i) { + out.append(" "); + } + out.println(node.getArtifact()); + } + for (DependencyNode c : node.getChildren()) { + printTree(c, depth + 1, out); + } + } +} diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 596389e3ead50e..21353b963305f8 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -40,15 +40,15 @@ 2.0.2 1.0 3.12.0 - 30.1-jre + 30.1.1-jre 1.2.6 - 1.0.5 + 1.0.9 1.1.0.Final 1.7.30 21.0.0 2.6.0 3.0.0-M5 - 1.5.0 + 1.6.0 6.8.3 @@ -492,6 +492,8 @@ + + .cache eclipse-format.xml LF ${format.skip} @@ -502,6 +504,8 @@ impsort-maven-plugin 1.6.0 + + .cache true ${format.skip} @@ -543,7 +547,6 @@ true true true - true clean install diff --git a/independent-projects/enforcer-rules/pom.xml b/independent-projects/enforcer-rules/pom.xml index 2f74ec7a213d08..222c7f9f205cde 100644 --- a/independent-projects/enforcer-rules/pom.xml +++ b/independent-projects/enforcer-rules/pom.xml @@ -95,6 +95,8 @@ + + .cache eclipse-format.xml LF ${format.skip} @@ -105,6 +107,8 @@ impsort-maven-plugin 1.6.0 + + .cache true ${format.skip} @@ -145,7 +149,6 @@ true true true - true true diff --git a/independent-projects/enforcer-rules/src/it/smoketest/integration-tests/ext1/pom.xml b/independent-projects/enforcer-rules/src/it/smoketest/integration-tests/ext1/pom.xml index b7a1c45ee91d5f..b8520fe7e0344e 100644 --- a/independent-projects/enforcer-rules/src/it/smoketest/integration-tests/ext1/pom.xml +++ b/independent-projects/enforcer-rules/src/it/smoketest/integration-tests/ext1/pom.xml @@ -36,6 +36,11 @@ SuperfluousDeploymentDep + + + quarkus-enforcer-rules-smoketest-ext3-deployment + + io.quarkus @@ -50,6 +55,19 @@ + + io.quarkus + quarkus-enforcer-rules-smoketest-ext3-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/independent-projects/enforcer-rules/src/main/java/io/quarkus/enforcer/RequiresMinimalDeploymentDependency.java b/independent-projects/enforcer-rules/src/main/java/io/quarkus/enforcer/RequiresMinimalDeploymentDependency.java index 7c951a830317a5..8f73f2bf542037 100644 --- a/independent-projects/enforcer-rules/src/main/java/io/quarkus/enforcer/RequiresMinimalDeploymentDependency.java +++ b/independent-projects/enforcer-rules/src/main/java/io/quarkus/enforcer/RequiresMinimalDeploymentDependency.java @@ -1,5 +1,7 @@ package io.quarkus.enforcer; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -75,13 +77,20 @@ public void execute(MavenProject project, Map nonDeploymentArt + requiredDeps); } if (!existingUnmatchedDeploymentDeps.isEmpty()) { - String superfluousDeps = existingUnmatchedDeploymentDeps.stream() - .map(gav -> " " + gav) - .sorted() - .collect(Collectors.joining("\n")); - throw new EnforcerRuleException(existingUnmatchedDeploymentDeps.size() - + " minimal *-deployment dependencies are superfluous and must be removed from pom.xml:\n" - + superfluousDeps); + Set nonSuperfluous = parseNonSuperfluosArtifactIdsFromProperty(project); + if (!nonSuperfluous.isEmpty()) { + existingUnmatchedDeploymentDeps + .removeIf(gav -> nonSuperfluous.stream().anyMatch(aid -> gav.contains(":" + aid + ":"))); + } + if (!existingUnmatchedDeploymentDeps.isEmpty()) { + String superfluousDeps = existingUnmatchedDeploymentDeps.stream() + .map(gav -> " " + gav) + .sorted() + .collect(Collectors.joining("\n")); + throw new EnforcerRuleException(existingUnmatchedDeploymentDeps.size() + + " minimal *-deployment dependencies are superfluous and must be removed from pom.xml:\n" + + superfluousDeps); + } } } @@ -90,4 +99,13 @@ private boolean isMinDeploymentDepPresent(String deploymentGAV, String projArtif return deploymentGAV.equals(projArtifactKey) // special case: current project itself is the "required dependency" || existingDeploymentDeps.remove(deploymentGAV); } + + private Set parseNonSuperfluosArtifactIdsFromProperty(MavenProject project) { + String propValue = project.getProperties().getProperty("enforcer.requiresMinimalDeploymentDependency.nonSuperfluous"); + if (propValue != null) { + return Arrays.stream(propValue.split(",")).map(String::trim).collect(Collectors.toSet()); + } else { + return Collections.emptySet(); + } + } } diff --git a/independent-projects/ide-config/pom.xml b/independent-projects/ide-config/pom.xml index a720f7613cf471..4fdc9edd739ecb 100644 --- a/independent-projects/ide-config/pom.xml +++ b/independent-projects/ide-config/pom.xml @@ -85,7 +85,6 @@ true true true - true clean install diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/IncludeSectionHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/IncludeSectionHelper.java index ed8e2c100d13f1..b3a0b594973130 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/IncludeSectionHelper.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/IncludeSectionHelper.java @@ -13,6 +13,7 @@ public class IncludeSectionHelper implements SectionHelper { + static final String DEFAULT_NAME = "$default$"; private static final String TEMPLATE = "template"; private final Supplier