From ab6699ed391aa52dbeb55a1b9a58b727374cde4a Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Mon, 12 Apr 2021 11:28:18 +0200 Subject: [PATCH] Basic changes to integrate the new platform model based on the decomposed 'universe' BOM --- .../quarkus/deployment/BootstrapConfig.java | 11 + .../quarkus/deployment/QuarkusAugmentor.java | 6 +- .../builditem/AppModelProviderBuildItem.java | 37 ++ .../deployment/dev/IDEDevModeMain.java | 4 +- .../steps/CurateOutcomeBuildStep.java | 16 + .../src/main/java/io/quarkus/cli/Create.java | 2 +- .../java/io/quarkus/cli/QuarkusCliUtils.java | 3 +- devtools/gradle/pom.xml | 6 - .../GradleGroovyProjectBuildFile.java | 11 + .../GradleKotlinProjectBuildFile.java | 11 + .../gradle/AppModelGradleResolver.java | 2 +- .../gradle/builder/QuarkusModelBuilder.java | 88 ++--- .../extension/QuarkusPluginExtension.java | 6 +- .../io/quarkus/bootstrap/model/AppModel.java | 20 +- .../bootstrap/model/PersistentAppModel.java | 4 +- .../bootstrap/model/PlatformImports.java | 12 + .../bootstrap/model/PlatformImportsImpl.java | 200 ++++++++++ .../quarkus/bootstrap/model/PlatformInfo.java | 73 ++++ .../bootstrap/model/PlatformReleaseInfo.java | 89 +++++ .../bootstrap/model/PlatformStreamInfo.java | 78 ++++ .../model/gradle}/ArtifactCoords.java | 2 +- .../bootstrap/model/gradle}/Dependency.java | 2 +- .../model/gradle}/ModelParameter.java | 2 +- .../bootstrap/model/gradle}/QuarkusModel.java | 6 +- .../bootstrap/model/gradle}/SourceSet.java | 2 +- .../bootstrap/model/gradle}/Workspace.java | 2 +- .../model/gradle}/WorkspaceModule.java | 2 +- .../gradle}/impl/ArtifactCoordsImpl.java | 4 +- .../model/gradle}/impl/DependencyImpl.java | 4 +- .../gradle}/impl/ModelParameterImpl.java | 4 +- .../model/gradle}/impl/QuarkusModelImpl.java | 28 +- .../model/gradle}/impl/SourceSetImpl.java | 4 +- .../model/gradle}/impl/WorkspaceImpl.java | 8 +- .../gradle}/impl/WorkspaceModuleImpl.java | 8 +- .../bootstrap/model/PlatformImportsTest.java | 155 ++++++++ .../bootstrap/model/PlatformInfoTest.java | 43 +++ .../model/PlatformStreamInfoTest.java | 32 ++ .../io/quarkus/bootstrap/IDELauncherImpl.java | 4 +- .../bootstrap/utils/BuildToolHelper.java | 2 +- .../resolver/QuarkusGradleModelFactory.java | 2 +- .../resolver/QuarkusModelBuildAction.java | 4 +- .../bootstrap/util/QuarkusModelHelper.java | 15 +- .../resolver/BootstrapAppModelResolver.java | 53 +-- .../base/build-layout.include.qute | 3 + .../gradle/base/build-layout.include.qute | 3 + .../buildtool/maven/base/pom.tpl.qute.xml | 9 + .../codestarts/CodestartProjectInput.java | 6 + .../CodestartProjectInputBuilder.java | 6 + .../codestarts/core/CodestartData.java | 6 +- .../DefaultCodestartProjectDefinition.java | 2 +- .../quarkus/QuarkusCodestartProjectInput.java | 6 + .../QuarkusCodestartProjectInputBuilder.java | 7 + .../handlers/CreateProjectCommandHandler.java | 79 +++- .../project/QuarkusProjectHelper.java | 11 +- .../AbstractGroovyGradleBuildFile.java | 11 + .../devtools/project/buildfile/BuildFile.java | 4 +- .../project/buildfile/MavenBuildFile.java | 26 ++ .../buildfile/MavenProjectBuildFile.java | 55 ++- .../registry/ExtensionCatalogResolver.java | 86 ++++- .../io/quarkus/registry/union/Element.java | 20 + .../registry/union/ElementCatalog.java | 35 ++ .../registry/union/ElementCatalogBuilder.java | 361 ++++++++++++++++++ .../io/quarkus/registry/union/Member.java | 42 ++ .../java/io/quarkus/registry/union/Union.java | 29 ++ .../quarkus/registry/union/UnionVersion.java | 5 + .../builder/QuarkusModelBuilderTest.java | 8 +- .../jacoco/deployment/JacocoProcessor.java | 4 +- .../test/junit/IntegrationTestUtil.java | 2 +- .../test/junit/QuarkusTestExtension.java | 2 +- 69 files changed, 1669 insertions(+), 226 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/AppModelProviderBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/steps/CurateOutcomeBuildStep.java create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImports.java create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformInfo.java create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformReleaseInfo.java create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformStreamInfo.java rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/ArtifactCoords.java (79%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/Dependency.java (85%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/ModelParameter.java (67%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/QuarkusModel.java (64%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/SourceSet.java (76%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/Workspace.java (81%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/WorkspaceModule.java (83%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/ArtifactCoordsImpl.java (94%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/DependencyImpl.java (96%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/ModelParameterImpl.java (72%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/QuarkusModelImpl.java (58%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/SourceSetImpl.java (92%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/WorkspaceImpl.java (80%) rename independent-projects/bootstrap/{gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model => app-model/src/main/java/io/quarkus/bootstrap/model/gradle}/impl/WorkspaceModuleImpl.java (90%) create mode 100644 independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java create mode 100644 independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformInfoTest.java create mode 100644 independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformStreamInfoTest.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Element.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalog.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalogBuilder.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Member.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Union.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/UnionVersion.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/BootstrapConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/BootstrapConfig.java index 6762d8d11a288..b3979e5751025 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/BootstrapConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/BootstrapConfig.java @@ -17,4 +17,15 @@ public class BootstrapConfig { @ConfigItem(defaultValue = "false") boolean effectiveModelBuilder; + /** + * Whether to throw an error, warn or silently ignore misaligned platform BOM imports + */ + @ConfigItem(defaultValue = "error") + public MisalignedPlatformImports misalignedPlatformImports; + + public enum MisalignedPlatformImports { + ERROR, + WARN, + IGNORE; + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java index 6309d1ebe99a3..edb9880e8f1bc 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java @@ -24,6 +24,7 @@ import io.quarkus.builder.BuildResult; import io.quarkus.builder.item.BuildItem; import io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem; +import io.quarkus.deployment.builditem.AppModelProviderBuildItem; import io.quarkus.deployment.builditem.ArchiveRootBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; @@ -33,7 +34,6 @@ import io.quarkus.deployment.builditem.RawCommandLineArgumentsBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; -import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.deployment.pkg.builditem.DeploymentResultBuildItem; import io.quarkus.dev.spi.DevModeType; import io.quarkus.runtime.LaunchMode; @@ -117,7 +117,7 @@ public BuildResult run() throws Exception { .addInitial(LiveReloadBuildItem.class) .addInitial(AdditionalApplicationArchiveBuildItem.class) .addInitial(BuildSystemTargetBuildItem.class) - .addInitial(CurateOutcomeBuildItem.class); + .addInitial(AppModelProviderBuildItem.class); for (Class i : finalResults) { chainBuilder.addFinal(i); } @@ -146,7 +146,7 @@ public BuildResult run() throws Exception { devModeType == null ? Optional.empty() : Optional.of(devModeType), auxiliaryApplication)) .produce(new BuildSystemTargetBuildItem(targetDir, baseName, rebuild, buildSystemProperties == null ? new Properties() : buildSystemProperties)) - .produce(new CurateOutcomeBuildItem(effectiveModel)); + .produce(new AppModelProviderBuildItem(effectiveModel)); for (PathsCollection i : additionalApplicationArchives) { execBuilder.produce(new AdditionalApplicationArchiveBuildItem(i)); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/AppModelProviderBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/AppModelProviderBuildItem.java new file mode 100644 index 0000000000000..30784eb6f4f8d --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/AppModelProviderBuildItem.java @@ -0,0 +1,37 @@ +package io.quarkus.deployment.builditem; + +import org.jboss.logging.Logger; + +import io.quarkus.bootstrap.model.AppModel; +import io.quarkus.bootstrap.model.PlatformImports; +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.deployment.BootstrapConfig; + +public final class AppModelProviderBuildItem extends SimpleBuildItem { + + private static final Logger log = Logger.getLogger(AppModelProviderBuildItem.class); + + private final AppModel appModel; + + public AppModelProviderBuildItem(AppModel appModel) { + this.appModel = appModel; + } + + public AppModel validateAndGet(BootstrapConfig config) { + final PlatformImports platforms = appModel.getPlatforms(); + if (platforms != null && !BootstrapConfig.MisalignedPlatformImports.IGNORE.equals(config.misalignedPlatformImports) + && !platforms.isAligned()) { + switch (config.misalignedPlatformImports) { + case ERROR: + throw new RuntimeException(platforms.getMisalignmentReport()); + case WARN: + log.warn(platforms.getMisalignmentReport()); + break; + default: + throw new RuntimeException("Unrecognized option for quarkus.bootstrap.misaligned-platform-imports: " + + config.misalignedPlatformImports); + } + } + return appModel; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java index 26682bd9fe7a5..c4ba1c0778c19 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java @@ -14,11 +14,11 @@ import io.quarkus.bootstrap.BootstrapGradleException; import io.quarkus.bootstrap.app.CuratedApplication; import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; import io.quarkus.bootstrap.resolver.maven.workspace.LocalWorkspace; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; import io.quarkus.bootstrap.util.QuarkusModelHelper; import io.quarkus.bootstrap.utils.BuildToolHelper; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/CurateOutcomeBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/CurateOutcomeBuildStep.java new file mode 100644 index 0000000000000..efdaf75ffdcf7 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/CurateOutcomeBuildStep.java @@ -0,0 +1,16 @@ +package io.quarkus.deployment.steps; + +import io.quarkus.deployment.BootstrapConfig; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.AppModelProviderBuildItem; +import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; + +public class CurateOutcomeBuildStep { + + BootstrapConfig config; + + @BuildStep + CurateOutcomeBuildItem curateOutcome(AppModelProviderBuildItem appModelProvider) { + return new CurateOutcomeBuildItem(appModelProvider.validateAndGet(config)); + } +} diff --git a/devtools/cli/src/main/java/io/quarkus/cli/Create.java b/devtools/cli/src/main/java/io/quarkus/cli/Create.java index a1decff74abdc..df01891e85ba3 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Create.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Create.java @@ -49,7 +49,7 @@ static class TargetBuildTool { boolean gradle = false; @CommandLine.Option(names = { - "--grade-kotlin-dsl" }, order = 7, description = "Create a Gradle Kotlin DSL project.") + "--gradle-kotlin-dsl" }, order = 7, description = "Create a Gradle Kotlin DSL project.") boolean gradleKotlinDsl = false; } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCliUtils.java b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCliUtils.java index 8649029f2909e..13fa57d06f33a 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCliUtils.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCliUtils.java @@ -30,7 +30,6 @@ static QuarkusProject getQuarkusProject(BuildTool buildTool, Path projectRoot) { } private static QuarkusProject getNonMavenProject(Path projectRoot, BuildTool buildTool) { - return QuarkusProjectHelper.getProject(projectRoot, buildTool, - QuarkusCliVersion.version()); + return QuarkusProjectHelper.getProject(projectRoot, buildTool, null); } } diff --git a/devtools/gradle/pom.xml b/devtools/gradle/pom.xml index d4de2d4396980..9d00fe6c5dbc5 100644 --- a/devtools/gradle/pom.xml +++ b/devtools/gradle/pom.xml @@ -26,12 +26,6 @@ io.quarkus quarkus-bootstrap-core - - - io.quarkus - quarkus-bootstrap-maven-resolver - - io.quarkus diff --git a/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleGroovyProjectBuildFile.java b/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleGroovyProjectBuildFile.java index 2aa05f33cac59..b5e7673c0296d 100644 --- a/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleGroovyProjectBuildFile.java +++ b/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleGroovyProjectBuildFile.java @@ -25,6 +25,11 @@ String getBuildGradlePath() { return BUILD_GRADLE_PATH; } + @Override + protected boolean importBom(ArtifactCoords coords) { + return importBomInModel(getModel(), coords); + } + @Override protected boolean addDependency(ArtifactCoords coords, boolean managed) { return addDependencyInModel(getModel(), coords, managed); @@ -35,6 +40,12 @@ public BuildTool getBuildTool() { return BuildTool.GRADLE; } + static boolean importBomInModel(Model model, ArtifactCoords coords) { + return addDependencyInModel(model, + String.format(" implementation enforcedPlatform(%s)%n", + createDependencyCoordinatesString(coords, false, '\''))); + } + static boolean addDependencyInModel(Model model, ArtifactCoords coords, boolean managed) { return addDependencyInModel(model, String.format(" implementation %s%n", createDependencyCoordinatesString(coords, managed, '\''))); diff --git a/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleKotlinProjectBuildFile.java b/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleKotlinProjectBuildFile.java index f959d2b17dc27..8dc4ff28f4a5f 100644 --- a/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleKotlinProjectBuildFile.java +++ b/devtools/gradle/src/main/java/io/quarkus/devtools/project/buildfile/GradleKotlinProjectBuildFile.java @@ -25,6 +25,11 @@ String getBuildGradlePath() { return BUILD_GRADLE_PATH; } + @Override + protected boolean importBom(ArtifactCoords coords) { + return importBomInModel(getModel(), coords); + } + @Override protected boolean addDependency(ArtifactCoords coords, boolean managed) { return addDependencyInModel(getModel(), coords, managed); @@ -35,6 +40,12 @@ public BuildTool getBuildTool() { return BuildTool.GRADLE_KOTLIN_DSL; } + static boolean importBomInModel(Model model, ArtifactCoords coords) { + return addDependencyInModel(model, + String.format(" implementation enforcedPlatform(%s)%n", + createDependencyCoordinatesString(coords, false, '\''))); + } + static boolean addDependencyInModel(Model model, ArtifactCoords coords, boolean managed) { return addDependencyInModel(model, String.format(" implementation(%s)%n", createDependencyCoordinatesString(coords, managed, '"'))); diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java b/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java index 4760563d618ae..0e1ccd52c6f43 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java @@ -17,9 +17,9 @@ import io.quarkus.bootstrap.model.AppArtifactKey; import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppModel; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import io.quarkus.bootstrap.resolver.AppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolverException; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; import io.quarkus.bootstrap.util.QuarkusModelHelper; public class AppModelGradleResolver implements AppModelResolver { 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 203f410d53518..8b56fd4336427 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 @@ -1,18 +1,13 @@ package io.quarkus.gradle.builder; -import static io.quarkus.bootstrap.resolver.model.impl.ArtifactCoordsImpl.TYPE_JAR; - import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -46,19 +41,21 @@ import org.gradle.tooling.provider.model.ParameterizedToolingModelBuilder; import io.quarkus.bootstrap.BootstrapConstants; -import io.quarkus.bootstrap.model.AppArtifactKey; -import io.quarkus.bootstrap.resolver.model.ArtifactCoords; -import io.quarkus.bootstrap.resolver.model.Dependency; -import io.quarkus.bootstrap.resolver.model.ModelParameter; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; -import io.quarkus.bootstrap.resolver.model.impl.ArtifactCoordsImpl; -import io.quarkus.bootstrap.resolver.model.impl.DependencyImpl; -import io.quarkus.bootstrap.resolver.model.impl.ModelParameterImpl; -import io.quarkus.bootstrap.resolver.model.impl.QuarkusModelImpl; -import io.quarkus.bootstrap.resolver.model.impl.SourceSetImpl; -import io.quarkus.bootstrap.resolver.model.impl.WorkspaceImpl; -import io.quarkus.bootstrap.resolver.model.impl.WorkspaceModuleImpl; +import io.quarkus.bootstrap.model.PlatformImports; +import io.quarkus.bootstrap.model.PlatformImportsImpl; +import io.quarkus.bootstrap.model.gradle.ArtifactCoords; +import io.quarkus.bootstrap.model.gradle.Dependency; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; +import io.quarkus.bootstrap.model.gradle.impl.ArtifactCoordsImpl; +import io.quarkus.bootstrap.model.gradle.impl.DependencyImpl; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; +import io.quarkus.bootstrap.model.gradle.impl.QuarkusModelImpl; +import io.quarkus.bootstrap.model.gradle.impl.SourceSetImpl; +import io.quarkus.bootstrap.model.gradle.impl.WorkspaceImpl; +import io.quarkus.bootstrap.model.gradle.impl.WorkspaceModuleImpl; +import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.util.QuarkusModelHelper; import io.quarkus.gradle.QuarkusPlugin; import io.quarkus.gradle.tasks.QuarkusGradleUtils; @@ -103,7 +100,7 @@ public Object buildAll(String modelName, ModelParameter parameter, Project proje final List deploymentDeps = getEnforcedPlatforms(project); - final Map platformProperties = resolvePlatformProperties(project, deploymentDeps); + final PlatformImports platformImports = resolvePlatformImports(project, deploymentDeps); final Map appDependencies = new LinkedHashMap<>(); final Set visitedDeps = new HashSet<>(); @@ -124,23 +121,20 @@ public Object buildAll(String modelName, ModelParameter parameter, Project proje extensionDependencies, deploymentDeps.stream().map(QuarkusModelBuilder::toEnforcedPlatformDependency) .filter(Objects::nonNull).collect(Collectors.toList()), - platformProperties); + platformImports); } - private Map resolvePlatformProperties(Project project, + private PlatformImports resolvePlatformImports(Project project, List deploymentDeps) { final Configuration boms = project.getConfigurations() .detachedConfiguration(deploymentDeps.toArray(new org.gradle.api.artifacts.Dependency[0])); - final Map platformProps = new HashMap<>(); - final Set descriptorKeys = new HashSet<>(4); - final Set propertyKeys = new HashSet<>(2); + final PlatformImportsImpl platformImports = new PlatformImportsImpl(); boms.getResolutionStrategy().eachDependency(d -> { final String group = d.getTarget().getGroup(); final String name = d.getTarget().getName(); if (name.endsWith(BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX)) { - descriptorKeys.add(new AppArtifactKey(group, - name.substring(0, name.length() - BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX.length()), - d.getTarget().getVersion())); + platformImports.addPlatformDescriptor(group, name, d.getTarget().getVersion(), "json", + d.getTarget().getVersion()); } else if (name.endsWith(BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX)) { final DefaultDependencyArtifact dep = new DefaultDependencyArtifact(); dep.setExtension("properties"); @@ -154,44 +148,20 @@ private Map resolvePlatformProperties(Project project, for (ResolvedArtifact a : project.getConfigurations().detachedConfiguration(gradleDep) .getResolvedConfiguration().getResolvedArtifacts()) { if (a.getName().equals(name)) { - final Properties props = new Properties(); - try (InputStream is = new FileInputStream(a.getFile())) { - props.load(is); - } catch (IOException e) { - throw new GradleException("Failed to read properties from " + a.getFile(), e); - } - for (Map.Entry prop : props.entrySet()) { - final String propName = String.valueOf(prop.getKey()); - if (propName.startsWith(BootstrapConstants.PLATFORM_PROPERTY_PREFIX)) { - platformProps.put(propName, String.valueOf(prop.getValue())); - } + try { + platformImports.addPlatformProperties(group, name, null, "properties", d.getTarget().getVersion(), + a.getFile().toPath()); + } catch (AppModelResolverException e) { + throw new GradleException("Failed to import platform properties " + a.getFile(), e); } break; } } - propertyKeys.add(new AppArtifactKey(group, - name.substring(0, name.length() - BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX.length()), - d.getTarget().getVersion())); } }); boms.getResolvedConfiguration(); - if (!descriptorKeys.containsAll(propertyKeys)) { - final StringBuilder buf = new StringBuilder(); - buf.append( - "The Quarkus platform properties applied to the project are missing the corresponding Quarkus platform BOM imports:"); - final int l = buf.length(); - for (AppArtifactKey key : propertyKeys) { - if (!descriptorKeys.contains(key)) { - if (l - buf.length() < 0) { - buf.append(','); - } - buf.append(' ').append(key); - } - } - throw new GradleException(buf.toString()); - } - return platformProps; + return platformImports; } public Set getWorkspace(Project project, LaunchMode mode, ArtifactCoords mainModuleCoord) { @@ -484,7 +454,7 @@ private SourceSetImpl convert(SourceSet sourceSet) { return new SourceSetImpl(existingSrcDirs); } - private io.quarkus.bootstrap.resolver.model.SourceSet getSourceSourceSet(SourceSet sourceSet) { + private io.quarkus.bootstrap.model.gradle.SourceSet getSourceSourceSet(SourceSet sourceSet) { return new SourceSetImpl(sourceSet.getAllJava().getSrcDirs(), sourceSet.getResources().getSourceDirectories().getSingleFile()); } @@ -527,6 +497,6 @@ private static DependencyImpl initDependency(ResolvedArtifact a) { private static ArtifactCoords toAppDependenciesKey(String groupId, String artifactId, String classifier) { // Default classifier is empty string and not null value, lets keep it that way classifier = classifier == null ? "" : classifier; - return new ArtifactCoordsImpl(groupId, artifactId, classifier, "", TYPE_JAR); + return new ArtifactCoordsImpl(groupId, artifactId, classifier, "", ArtifactCoordsImpl.TYPE_JAR); } } diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java index 203546f978a7e..87af073fb1d0d 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java @@ -26,10 +26,10 @@ import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppModel; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; import io.quarkus.bootstrap.resolver.AppModelResolver; -import io.quarkus.bootstrap.resolver.model.ModelParameter; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.impl.ModelParameterImpl; import io.quarkus.gradle.AppModelGradleResolver; import io.quarkus.gradle.builder.QuarkusModelBuilder; import io.quarkus.gradle.tasks.QuarkusGradleUtils; diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppModel.java index 9af2500d57098..453258db877f4 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppModel.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppModel.java @@ -60,7 +60,7 @@ public class AppModel implements Serializable { */ private final Set localProjectArtifacts; - private final Map platformProperties; + private final PlatformImports platformImports; private final Map capabilitiesContracts; @@ -73,13 +73,17 @@ private AppModel(Builder builder) { this.runnerParentFirstArtifacts = builder.runnerParentFirstArtifacts; this.lesserPriorityArtifacts = builder.lesserPriorityArtifacts; this.localProjectArtifacts = builder.localProjectArtifacts; - this.platformProperties = builder.platformProperties; + this.platformImports = builder.platformImports; this.capabilitiesContracts = builder.capabilitiesContracts; log.debugf("Created AppModel %s", this); } public Map getPlatformProperties() { - return platformProperties; + return platformImports == null ? Collections.emptyMap() : platformImports.getPlatformProperties(); + } + + public PlatformImports getPlatforms() { + return platformImports; } public AppArtifact getAppArtifact() { @@ -150,7 +154,7 @@ public static class Builder { private final Set excludedArtifacts = new HashSet<>(); private final Set lesserPriorityArtifacts = new HashSet<>(); private final Set localProjectArtifacts = new HashSet<>(); - private Map platformProperties = Collections.emptyMap(); + private PlatformImports platformImports; private Map capabilitiesContracts = Collections.emptyMap(); private Predicate depPredicate; @@ -160,12 +164,8 @@ public Builder setAppArtifact(AppArtifact appArtifact) { return this; } - public Builder addPlatformProperties(Map platformProperties) { - if (this.platformProperties.isEmpty()) { - this.platformProperties = platformProperties; - } else { - this.platformProperties.putAll(platformProperties); - } + public Builder setPlatformImports(PlatformImports platformImports) { + this.platformImports = platformImports; return this; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PersistentAppModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PersistentAppModel.java index 5a0efa6bd7b24..203276a573130 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PersistentAppModel.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PersistentAppModel.java @@ -85,7 +85,9 @@ public AppModel getAppModel(Path root) { for (AppArtifactKey i : localProjectArtifacts) { model.addLocalProjectArtifact(i); } - model.addPlatformProperties(platformProperties); + final PlatformImportsImpl pi = new PlatformImportsImpl(); + pi.setPlatformProperties(platformProperties); + model.setPlatformImports(pi); return model.build(); } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImports.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImports.java new file mode 100644 index 0000000000000..b0a047f9c2d41 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImports.java @@ -0,0 +1,12 @@ +package io.quarkus.bootstrap.model; + +import java.util.Map; + +public interface PlatformImports { + + public Map getPlatformProperties(); + + public String getMisalignmentReport(); + + public boolean isAligned(); +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java new file mode 100644 index 0000000000000..1524a2c09acf3 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java @@ -0,0 +1,200 @@ +package io.quarkus.bootstrap.model; + +import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.resolver.AppModelResolverException; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class PlatformImportsImpl implements PlatformImports, Serializable { + + public static final String PROPERTY_PREFIX = "platform.release-info@"; + + public static final char PLATFORM_KEY_STREAM_SEPARATOR = '$'; + public static final char STREAM_VERSION_SEPARATOR = '#'; + + private static int requiredIndex(String s, char c, int fromIndex) { + final int i = s.indexOf(c, fromIndex); + if (i < 0) { + throw new IllegalArgumentException("Failed to locate '" + c + "' in '" + s + "'"); + } + return i; + } + + public static boolean isPlatformReleaseInfo(String s) { + return s != null && s.startsWith(PROPERTY_PREFIX); + } + + // metadata for each found platform release by platform key + private final Map allPlatformInfo = new HashMap<>(); + // imported platform BOMs by platform keys (groupId) + private final Map> importedPlatformBoms = new HashMap<>(); + + private final Map platformImports = new HashMap<>(); + + final Map collectedProps = new HashMap(); + + public PlatformImportsImpl() { + } + + void addPlatformRelease(String propertyName, String propertyValue) { + final int platformKeyStreamSep = requiredIndex(propertyName, PLATFORM_KEY_STREAM_SEPARATOR, PROPERTY_PREFIX.length()); + final int streamVersionSep = requiredIndex(propertyName, STREAM_VERSION_SEPARATOR, platformKeyStreamSep + 1); + + final String platformKey = propertyName.substring(PROPERTY_PREFIX.length(), platformKeyStreamSep); + final String streamId = propertyName.substring(platformKeyStreamSep + 1, streamVersionSep); + final String version = propertyName.substring(streamVersionSep + 1); + allPlatformInfo.computeIfAbsent(platformKey, k -> new PlatformInfo(k)).getOrCreateStream(streamId).addIfNotPresent( + version, + () -> new PlatformReleaseInfo(platformKey, streamId, version, propertyValue)); + } + + public void addPlatformDescriptor(String groupId, String artifactId, String classifier, String type, String version) { + final AppArtifactCoords bomCoords = new AppArtifactCoords(groupId, + artifactId.substring(0, + artifactId.length() - BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX.length()), + null, "pom", + version); + platformImports.computeIfAbsent(bomCoords, c -> new PlatformImport()).descriptorFound = true; + } + + public void addPlatformProperties(String groupId, String artifactId, String classifier, String type, String version, + Path propsPath) throws AppModelResolverException { + final AppArtifactCoords bomCoords = new AppArtifactCoords(groupId, + artifactId.substring(0, + artifactId.length() - BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX.length()), + null, "pom", + version); + platformImports.computeIfAbsent(bomCoords, c -> new PlatformImport()); + importedPlatformBoms.computeIfAbsent(groupId, g -> new ArrayList<>()).add(bomCoords); + + final Properties props = new Properties(); + try (InputStream is = Files.newInputStream(propsPath)) { + props.load(is); + } catch (IOException e) { + throw new AppModelResolverException("Failed to read properties from " + propsPath, e); + } + for (Map.Entry prop : props.entrySet()) { + final String name = String.valueOf(prop.getKey()); + if (name.startsWith(BootstrapConstants.PLATFORM_PROPERTY_PREFIX)) { + if (isPlatformReleaseInfo(name)) { + addPlatformRelease(name, String.valueOf(prop.getValue())); + } else { + collectedProps.putIfAbsent(name, String.valueOf(prop.getValue().toString())); + } + } + } + } + + public void setPlatformProperties(Map platformProps) { + this.collectedProps.putAll(platformProps); + } + + @Override + public Map getPlatformProperties() { + return collectedProps; + } + + @Override + public String getMisalignmentReport() { + StringWriter error = null; + for (Map.Entry pi : platformImports.entrySet()) { + if (!pi.getValue().descriptorFound) { + if (error == null) { + error = new StringWriter(); + error.append( + "The Quarkus platform properties applied to the project are missing the corresponding Quarkus platform BOM imports: "); + } else { + error.append(", "); + } + error.append(pi.getKey().toString()); + } + } + if (error != null) { + return error.getBuffer().toString(); + } + + final Map>> possibleAlignments = getPossibleAlignemnts(importedPlatformBoms); + if (possibleAlignments.isEmpty()) { + return null; + } + + error = new StringWriter(); + try (BufferedWriter writer = new BufferedWriter(error)) { + writer.append( + "Some of the imported Quarkus platform BOMs belong to different platform releases. To properly align the platform BOM imports, please, consider one of the following combinations:"); + writer.newLine(); + for (Map.Entry>> entry : possibleAlignments.entrySet()) { + writer.append("For platform ").append(entry.getKey()).append(':'); + writer.newLine(); + int i = 1; + for (List boms : entry.getValue()) { + writer.append(" ").append(String.valueOf(i++)).append(") "); + writer.newLine(); + for (String bom : boms) { + writer.append(" - ").append(bom); + writer.newLine(); + } + } + } + } catch (IOException e) { + // ignore + } + return error.toString(); + } + + @Override + public boolean isAligned() { + return isAligned(importedPlatformBoms); + } + + boolean isAligned(Map> importedPlatformBoms) { + for (Map.Entry pi : platformImports.entrySet()) { + if (!pi.getValue().descriptorFound) { + return false; + } + } + for (Map.Entry> platformImportedBoms : importedPlatformBoms.entrySet()) { + final PlatformInfo platformInfo = allPlatformInfo.get(platformImportedBoms.getKey()); + if (platformInfo != null && !platformInfo.isAligned(platformImportedBoms.getValue())) { + return false; + } + } + return true; + } + + private Map>> getPossibleAlignemnts( + Map> importedPlatformBoms) { + final Map>> alignments = new HashMap<>(importedPlatformBoms.size()); + for (Map.Entry> platformImportedBoms : importedPlatformBoms.entrySet()) { + final PlatformInfo platformInfo = allPlatformInfo.get(platformImportedBoms.getKey()); + if (platformInfo == null || platformInfo.isAligned(platformImportedBoms.getValue())) { + continue; + } + alignments.put(platformInfo.getPlatformKey(), platformInfo.getPossibleAlignments(platformImportedBoms.getValue())); + } + return alignments; + } + + Collection getPlatforms() { + return allPlatformInfo.values(); + } + + PlatformInfo getPlatform(String platformKey) { + return allPlatformInfo.get(platformKey); + } + + private static class PlatformImport implements Serializable { + boolean descriptorFound; + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformInfo.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformInfo.java new file mode 100644 index 0000000000000..348a9771f14cc --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformInfo.java @@ -0,0 +1,73 @@ +package io.quarkus.bootstrap.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class PlatformInfo implements Serializable { + + private final String key; + private final List streams = new ArrayList<>(1); // most of the time there will be only one + + public PlatformInfo(String key) { + this.key = key; + } + + public String getPlatformKey() { + return key; + } + + boolean isAligned(Collection importedBoms) { + if (streams.isEmpty()) { + return true; + } + if (streams.size() > 1) { + return false; + } + return streams.get(0).isAligned(importedBoms); + } + + List> getPossibleAlignments(Collection importedPlatformBoms) { + if (streams.size() > 1) { + final StringBuilder buf = new StringBuilder(); + buf.append("Imported BOMs "); + final Iterator it = importedPlatformBoms.iterator(); + if (it.hasNext()) { + buf.append(it.next()); + while (it.hasNext()) { + buf.append(", ").append(it.next()); + } + } + buf.append(" belong to different platform streams ").append(streams.get(0)); + for (int i = 1; i < streams.size(); ++i) { + buf.append(", ").append(streams.get(i)); + } + throw new RuntimeException(buf.append(" while only one stream per platform is allowed.").toString()); + } + return streams.get(0).getPossibleAlignemnts(importedPlatformBoms); + } + + PlatformStreamInfo getOrCreateStream(String stream) { + PlatformStreamInfo s = getStream(stream); + if (s == null) { + s = new PlatformStreamInfo(stream); + streams.add(s); + } + return s; + } + + Collection getStreams() { + return streams; + } + + PlatformStreamInfo getStream(String stream) { + for (PlatformStreamInfo s : streams) { + if (s.getId().equals(stream)) { + return s; + } + } + return null; + } +} \ No newline at end of file diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformReleaseInfo.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformReleaseInfo.java new file mode 100644 index 0000000000000..f316e8eaf62dc --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformReleaseInfo.java @@ -0,0 +1,89 @@ +package io.quarkus.bootstrap.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Platform release info that is encoded into a property in a platform properties artifact + * following the format {@code platform.release-info@$#=(,)} + */ +public class PlatformReleaseInfo implements Serializable { + + private final String platformKey; + private final String stream; + private final String version; + private final List boms; + + public PlatformReleaseInfo(String platformKey, String stream, String version, String boms) { + this.platformKey = platformKey; + this.stream = stream; + this.version = version; + final String[] bomCoords = boms.split(","); + this.boms = new ArrayList<>(bomCoords.length); + for (String s : bomCoords) { + this.boms.add(AppArtifactCoords.fromString(s)); + } + } + + /** + * The platform key. Could be the {@code groupId} of the stack, e.g. {@code io.quarkus.platform} + * + * @return platform key + */ + public String getPlatformKey() { + return platformKey; + } + + /** + * Platform stream. Could be the {@code major.minor} part of the platform release version. + * + * @return platform stream + */ + public String getStream() { + return stream; + } + + /** + * The version of the platform in a stream. Ideally, the micro version to make the comparisons easier. + * + * @return version in the stream + */ + public String getVersion() { + return version; + } + + /** + * Member BOM coordinates. + * + * @return member BOM coordinates + */ + public List getBoms() { + return boms; + } + + String getPropertyName() { + final StringBuilder buf = new StringBuilder(); + buf.append(PlatformImportsImpl.PROPERTY_PREFIX).append(platformKey) + .append(PlatformImportsImpl.PLATFORM_KEY_STREAM_SEPARATOR) + .append(stream) + .append(PlatformImportsImpl.STREAM_VERSION_SEPARATOR).append(version); + return buf.toString(); + } + + String getPropertyValue() { + final StringBuilder buf = new StringBuilder(); + final List boms = getBoms(); + if (!boms.isEmpty()) { + buf.append(boms.get(0).toString()); + for (int i = 1; i < boms.size(); ++i) { + buf.append(',').append(boms.get(i)); + } + } + return buf.toString(); + } + + public String toString() { + return getPropertyName() + '=' + getPropertyValue(); + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformStreamInfo.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformStreamInfo.java new file mode 100644 index 0000000000000..1cdf6c9f10cc6 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformStreamInfo.java @@ -0,0 +1,78 @@ +package io.quarkus.bootstrap.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class PlatformStreamInfo implements Serializable { + + private final String id; + private final Map releases = new HashMap<>(); + + public PlatformStreamInfo(String stream) { + this.id = stream; + } + + public String getId() { + return id; + } + + boolean isAligned(Collection importedBoms) { + if (releases.isEmpty()) { + return true; + } + for (PlatformReleaseInfo release : releases.values()) { + if (release.getBoms().containsAll(importedBoms)) { + return true; + } + } + return false; + } + + List> getPossibleAlignemnts(Collection importedPlatformBoms) { + final Map importedKeys = new HashMap<>(importedPlatformBoms.size()); + for (AppArtifactCoords bom : importedPlatformBoms) { + importedKeys.put(bom.getKey(), bom.getVersion()); + } + final List> suggestions = new ArrayList<>(); + for (PlatformReleaseInfo release : releases.values()) { + final Map stackBoms = new HashMap<>(release.getBoms().size()); + for (AppArtifactCoords bom : release.getBoms()) { + stackBoms.put(bom.getKey(), bom); + } + if (stackBoms.keySet().containsAll(importedKeys.keySet())) { + final List suggestion = new ArrayList<>(importedPlatformBoms.size()); + suggestions.add(suggestion); + for (Map.Entry bomKey : importedKeys.entrySet()) { + final AppArtifactCoords stackBom = stackBoms.get(bomKey.getKey()); + if (!bomKey.getValue().equals(stackBom.getVersion())) { + suggestion.add(bomKey.getKey().getGroupId() + ":" + bomKey.getKey().getArtifactId() + ":" + + bomKey.getValue() + " -> " + stackBom.getVersion()); + } else { + suggestion + .add(stackBom.getGroupId() + ":" + stackBom.getArtifactId() + ":" + stackBom.getVersion()); + } + } + } + } + return suggestions; + } + + void addIfNotPresent(String version, Supplier release) { + if (!releases.containsKey(version)) { + releases.put(version, release.get()); + } + } + + Collection getReleases() { + return releases.values(); + } + + PlatformReleaseInfo getRelease(String version) { + return releases.get(version); + } +} \ No newline at end of file diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ArtifactCoords.java similarity index 79% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ArtifactCoords.java index 2435025b8e94f..08f903903dde5 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ArtifactCoords.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; public interface ArtifactCoords { diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Dependency.java similarity index 85% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Dependency.java index ac58deec4eefa..89ab5a95506f2 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Dependency.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; import java.io.File; import java.util.Set; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ModelParameter.java similarity index 67% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ModelParameter.java index 8d6dc75e8284d..da569b6d7924b 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/ModelParameter.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; public interface ModelParameter { diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/QuarkusModel.java similarity index 64% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/QuarkusModel.java index 0bbce30921df5..70938352e5516 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/QuarkusModel.java @@ -1,7 +1,7 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; +import io.quarkus.bootstrap.model.PlatformImports; import java.util.List; -import java.util.Map; public interface QuarkusModel { @@ -13,5 +13,5 @@ public interface QuarkusModel { List getEnforcedPlatformDependencies(); - Map getPlatformProperties(); + PlatformImports getPlatformImports(); } diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/SourceSet.java similarity index 76% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/SourceSet.java index c90d35a06f23e..840f4d86f7937 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/SourceSet.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; import java.io.File; import java.util.Set; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Workspace.java similarity index 81% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Workspace.java index 89888c31aadb4..75b2166398944 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/Workspace.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; import java.util.Collection; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/WorkspaceModule.java similarity index 83% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/WorkspaceModule.java index 4b27f8c24d6e6..8ec440b8e58ea 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/WorkspaceModule.java @@ -1,4 +1,4 @@ -package io.quarkus.bootstrap.resolver.model; +package io.quarkus.bootstrap.model.gradle; import java.io.File; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ArtifactCoordsImpl.java similarity index 94% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ArtifactCoordsImpl.java index f18feb15d744a..784437f750d64 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ArtifactCoordsImpl.java @@ -1,6 +1,6 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.ArtifactCoords; +import io.quarkus.bootstrap.model.gradle.ArtifactCoords; import java.io.Serializable; import java.util.Objects; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/DependencyImpl.java similarity index 96% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/DependencyImpl.java index 3b75e0a14bcf5..20597ca0d74ad 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/DependencyImpl.java @@ -1,6 +1,6 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.Dependency; +import io.quarkus.bootstrap.model.gradle.Dependency; import java.io.File; import java.io.Serializable; import java.util.HashSet; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ModelParameterImpl.java similarity index 72% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ModelParameterImpl.java index 085680bcfb618..3c25f315ef246 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/ModelParameterImpl.java @@ -1,6 +1,6 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.ModelParameter; +import io.quarkus.bootstrap.model.gradle.ModelParameter; import java.io.Serializable; public class ModelParameterImpl implements ModelParameter, Serializable { diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/QuarkusModelImpl.java similarity index 58% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/QuarkusModelImpl.java index 4c6fdfc3facf4..f2f120440fd55 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/QuarkusModelImpl.java @@ -1,12 +1,11 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.Dependency; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.Workspace; +import io.quarkus.bootstrap.model.PlatformImports; +import io.quarkus.bootstrap.model.gradle.Dependency; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.Workspace; import java.io.Serializable; -import java.util.Collections; import java.util.List; -import java.util.Map; public class QuarkusModelImpl implements QuarkusModel, Serializable { @@ -14,25 +13,18 @@ public class QuarkusModelImpl implements QuarkusModel, Serializable { private final List appDependencies; private final List extensionDependencies; private final List enforcedPlatformDependencies; - private final Map platformProperties; - - public QuarkusModelImpl(Workspace workspace, - List appDependencies, - List extensionDependencies, - List enforcedPlatformDependencies) { - this(workspace, appDependencies, extensionDependencies, enforcedPlatformDependencies, Collections.emptyMap()); - } + private final PlatformImports platformImports; public QuarkusModelImpl(Workspace workspace, List appDependencies, List extensionDependencies, List enforcedPlatformDependencies, - Map platformProperties) { + PlatformImports platformImports) { this.workspace = workspace; this.appDependencies = appDependencies; this.extensionDependencies = extensionDependencies; this.enforcedPlatformDependencies = enforcedPlatformDependencies; - this.platformProperties = platformProperties; + this.platformImports = platformImports; } @Override @@ -56,7 +48,7 @@ public List getEnforcedPlatformDependencies() { } @Override - public Map getPlatformProperties() { - return platformProperties; + public PlatformImports getPlatformImports() { + return platformImports; } } diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/SourceSetImpl.java similarity index 92% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/SourceSetImpl.java index acb4d5e372345..040d03b46a14d 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/SourceSetImpl.java @@ -1,6 +1,6 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.SourceSet; +import io.quarkus.bootstrap.model.gradle.SourceSet; import java.io.File; import java.io.Serializable; import java.util.HashSet; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceImpl.java similarity index 80% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceImpl.java index 339aacaebc6f6..f12a6429c61ed 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceImpl.java @@ -1,8 +1,8 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.ArtifactCoords; -import io.quarkus.bootstrap.resolver.model.Workspace; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; +import io.quarkus.bootstrap.model.gradle.ArtifactCoords; +import io.quarkus.bootstrap.model.gradle.Workspace; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceModuleImpl.java similarity index 90% rename from independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceModuleImpl.java index 8827a6fd56687..b80e372515f46 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/gradle/impl/WorkspaceModuleImpl.java @@ -1,8 +1,8 @@ -package io.quarkus.bootstrap.resolver.model.impl; +package io.quarkus.bootstrap.model.gradle.impl; -import io.quarkus.bootstrap.resolver.model.ArtifactCoords; -import io.quarkus.bootstrap.resolver.model.SourceSet; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; +import io.quarkus.bootstrap.model.gradle.ArtifactCoords; +import io.quarkus.bootstrap.model.gradle.SourceSet; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import java.io.File; import java.io.Serializable; import java.util.Objects; diff --git a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java new file mode 100644 index 0000000000000..b04139d7c05e1 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java @@ -0,0 +1,155 @@ +package io.quarkus.bootstrap.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.bootstrap.util.IoUtils; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +public class PlatformImportsTest { + + private final List platformProps = new ArrayList<>(); + + @AfterEach + public void cleanUp() { + for (PlatformProps p : platformProps) { + p.delete(); + } + } + + @Test + public void singlePlatformReleaseInfo() throws Exception { + final PlatformProps props = newPlatformProps(); + props.setProperty("platform.quarkus.native.builder-image", "url"); + props.setRelease(new PlatformReleaseInfo("io.playground", "1.1", "1", + "io.playground:playground-bom::pom:1.1.1,io.playground:acme-bom::pom:2.2.2,io.playground:foo-bom::pom:3.3.3")); + + final PlatformImportsImpl pi = new PlatformImportsImpl(); + props.importRelease(pi); + + final PlatformInfo platform = pi.getPlatform("io.playground"); + assertNotNull(platform); + assertEquals("io.playground", platform.getPlatformKey()); + final PlatformStreamInfo stream = platform.getStream("1.1"); + assertNotNull(stream); + final PlatformReleaseInfo release = stream.getRelease("1"); + assertEquals("io.playground", release.getPlatformKey()); + assertEquals("1.1", release.getStream()); + assertEquals("1", release.getVersion()); + final List boms = release.getBoms(); + assertEquals(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"), + AppArtifactCoords.fromString("io.playground:foo-bom::pom:3.3.3")), boms); + assertEquals(1, stream.getReleases().size()); + assertEquals(1, platform.getStreams().size()); + assertEquals(1, pi.getPlatforms().size()); + + assertTrue(pi.isAligned(Collections.singletonMap("io.playground", + Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"))))); + assertFalse(pi.isAligned(Collections.singletonMap("io.playground", + Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"))))); + } + + @Test + public void multiplePlatformReleaseInTheSameStream() throws Exception { + final PlatformProps member1 = newPlatformProps(); + member1.setProperty("platform.quarkus.native.builder-image", "url"); + member1.setRelease(new PlatformReleaseInfo("io.playground", "1.1", "1", + "io.playground:playground-bom::pom:1.1.1,io.playground:acme-bom::pom:2.2.2,io.playground:foo-bom::pom:3.3.3")); + + final PlatformProps member2 = newPlatformProps(); + member2.setProperty("platform.quarkus.native.builder-image", "url"); + member2.setRelease(new PlatformReleaseInfo("io.playground", "1.1", "2", + "io.playground:playground-bom::pom:1.1.2,io.playground:acme-bom::pom:2.2.3,io.playground:foo-bom::pom:3.3.3")); + + final PlatformImportsImpl pi = new PlatformImportsImpl(); + member1.importRelease(pi); + member2.importRelease(pi); + + final PlatformInfo platform = pi.getPlatform("io.playground"); + assertNotNull(platform); + assertEquals("io.playground", platform.getPlatformKey()); + final PlatformStreamInfo stream = platform.getStream("1.1"); + assertNotNull(stream); + final PlatformReleaseInfo release = stream.getRelease("1"); + assertEquals("io.playground", release.getPlatformKey()); + assertEquals("1.1", release.getStream()); + assertEquals("1", release.getVersion()); + final List boms = release.getBoms(); + assertEquals(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"), + AppArtifactCoords.fromString("io.playground:foo-bom::pom:3.3.3")), boms); + assertEquals(2, stream.getReleases().size()); + assertEquals(1, platform.getStreams().size()); + assertEquals(1, pi.getPlatforms().size()); + + assertTrue(pi.isAligned(Collections.singletonMap("io.playground", + Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"))))); + assertTrue(pi.isAligned(Collections.singletonMap("io.playground", + Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.3"))))); + assertFalse(pi.isAligned(Collections.singletonMap("io.playground", + Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("io.playground:acme-bom::pom:2.2.2"))))); + } + + private PlatformProps newPlatformProps() throws IOException { + final PlatformProps p = new PlatformProps(); + platformProps.add(p); + return p; + } + + private static class PlatformProps { + + private final Path path; + private Properties props = new Properties(); + + private PlatformProps() throws IOException { + path = Files.createTempFile("quarkus", "platform-imports"); + } + + private void setRelease(PlatformReleaseInfo release) { + props.setProperty(release.getPropertyName(), release.getPropertyValue()); + } + + private void setProperty(String name, String value) { + props.setProperty(name, value); + } + + private void importRelease(PlatformImportsImpl pi) throws IOException { + try (BufferedWriter w = Files.newBufferedWriter(path)) { + props.store(w, "test playground platform props"); + } + props = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(path)) { + props.load(reader); + } + for (Map.Entry prop : props.entrySet()) { + if (PlatformImportsImpl.isPlatformReleaseInfo(prop.getKey().toString())) { + pi.addPlatformRelease(prop.getKey().toString(), prop.getValue().toString()); + } + } + } + + private void delete() { + IoUtils.recursiveDelete(path); + } + } +} diff --git a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformInfoTest.java b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformInfoTest.java new file mode 100644 index 0000000000000..8ba4d4eb73af6 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformInfoTest.java @@ -0,0 +1,43 @@ +package io.quarkus.bootstrap.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +public class PlatformInfoTest { + + @Test + public void emptyIsAligned() throws Exception { + assertTrue(new PlatformInfo("p") + .isAligned(Collections.singletonList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1")))); + } + + @Test + public void singleStreamIsAligned() throws Exception { + final PlatformInfo platform = new PlatformInfo("p"); + final PlatformStreamInfo stream = platform.getOrCreateStream("1.1"); + stream.addIfNotPresent("1", () -> new PlatformReleaseInfo("io.playground", "playground-bom", "1.1", + "io.playground:playground-bom::pom:1.1.1,org.acme:acme-bom::pom:2.2.2,com.foo:foo-bom::pom:3.3.3")); + stream.addIfNotPresent("2", () -> new PlatformReleaseInfo("io.playground", "playground-bom", "1.1", + "io.playground:playground-bom::pom:1.1.2,org.acme:acme-bom::pom:2.2.3,com.foo:foo-bom::pom:3.3.3")); + + assertTrue(platform.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.2")))); + assertTrue(platform.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.3")))); + assertFalse(platform.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.2")))); + } + + @Test + public void multipleStreamsAreNotAligned() throws Exception { + final PlatformInfo platform = new PlatformInfo("p"); + platform.getOrCreateStream("1.1"); + platform.getOrCreateStream("1.2"); + assertFalse(platform + .isAligned(Collections.singletonList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1")))); + } +} diff --git a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformStreamInfoTest.java b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformStreamInfoTest.java new file mode 100644 index 0000000000000..75e83c763bd0d --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformStreamInfoTest.java @@ -0,0 +1,32 @@ +package io.quarkus.bootstrap.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +public class PlatformStreamInfoTest { + + @Test + public void emptyIsAligned() throws Exception { + assertTrue(new PlatformStreamInfo("1.1") + .isAligned(Collections.singletonList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1")))); + } + + @Test + public void isAligned() throws Exception { + final PlatformStreamInfo stream = new PlatformStreamInfo("1.1"); + stream.addIfNotPresent("1", () -> new PlatformReleaseInfo("io.playground", "playground-bom", "1.1", + "io.playground:playground-bom::pom:1.1.1,org.acme:acme-bom::pom:2.2.2,com.foo:foo-bom::pom:3.3.3")); + stream.addIfNotPresent("2", () -> new PlatformReleaseInfo("io.playground", "playground-bom", "1.1", + "io.playground:playground-bom::pom:1.1.2,org.acme:acme-bom::pom:2.2.3,com.foo:foo-bom::pom:3.3.3")); + assertTrue(stream.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.1"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.2")))); + assertTrue(stream.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.3")))); + assertFalse(stream.isAligned(Arrays.asList(AppArtifactCoords.fromString("io.playground:playground-bom::pom:1.1.2"), + AppArtifactCoords.fromString("org.acme:acme-bom::pom:2.2.2")))); + } +} diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java index 8a549683d15e6..460b2ae317037 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java @@ -5,11 +5,11 @@ import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.devmode.DependenciesFilter; import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; import io.quarkus.bootstrap.util.QuarkusModelHelper; import io.quarkus.bootstrap.utils.BuildToolHelper; import java.io.Closeable; diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java index 6902f21a108e4..34d44ef2d66ba 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java @@ -4,9 +4,9 @@ import static io.quarkus.bootstrap.util.QuarkusModelHelper.ENABLE_JAR_PACKAGING; import static io.quarkus.bootstrap.util.QuarkusModelHelper.TEST_REQUIRED_TASKS; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.QuarkusGradleModelFactory; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; import io.quarkus.bootstrap.util.QuarkusModelHelper; import java.io.IOException; import java.nio.file.Files; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java index 46db36ca3cb52..3c8c76ee24b1c 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java +++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java @@ -1,6 +1,6 @@ package io.quarkus.bootstrap.resolver; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import java.io.File; import java.util.Collections; import java.util.List; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java index b021b1b1c5230..94d5f6099482c 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java +++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java @@ -1,7 +1,7 @@ package io.quarkus.bootstrap.resolver; -import io.quarkus.bootstrap.resolver.model.ModelParameter; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import java.io.Serializable; import org.gradle.tooling.BuildAction; import org.gradle.tooling.BuildController; diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java index e8a3585bd59d0..9d5ed81c2c98c 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java +++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java @@ -8,11 +8,11 @@ import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.model.CapabilityContract; import io.quarkus.bootstrap.model.PathsCollection; +import io.quarkus.bootstrap.model.gradle.ArtifactCoords; +import io.quarkus.bootstrap.model.gradle.Dependency; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import io.quarkus.bootstrap.resolver.AppModelResolverException; -import io.quarkus.bootstrap.resolver.model.ArtifactCoords; -import io.quarkus.bootstrap.resolver.model.Dependency; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -158,14 +158,11 @@ public static AppModel convert(QuarkusModel model, AppArtifact appArtifact) thro new AppArtifactKey(coords.getGroupId(), coords.getArtifactId(), null, coords.getType())); } - if (!model.getPlatformProperties().isEmpty()) { - appBuilder.addPlatformProperties(model.getPlatformProperties()); - } - appBuilder.addRuntimeDeps(userDeps) .addFullDeploymentDeps(fullDeploymentDeps) .addDeploymentDeps(deploymentDeps) - .setAppArtifact(appArtifact); + .setAppArtifact(appArtifact) + .setPlatformImports(model.getPlatformImports()); return appBuilder.build(); } 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 28193461c84ac..1b1baed07dc9d 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 @@ -7,22 +7,17 @@ import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.model.PathsCollection; +import io.quarkus.bootstrap.model.PlatformImportsImpl; import io.quarkus.bootstrap.resolver.maven.BuildDependencyGraphVisitor; import io.quarkus.bootstrap.resolver.maven.DeploymentInjectingDependencyVisitor; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.SimpleDependencyGraphTransformationContext; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.function.Consumer; import org.eclipse.aether.RepositoryException; @@ -268,56 +263,22 @@ private AppModel doResolveModel(AppArtifact appArtifact, List direct private void collectPlatformProperties(AppModel.Builder appBuilder, List managedDeps) throws AppModelResolverException { - final Set descriptorKeys = new HashSet<>(4); - final Set propertyKeys = new HashSet<>(2); - final Map collectedProps = new HashMap(); + final PlatformImportsImpl platformReleases = new PlatformImportsImpl(); for (Dependency d : managedDeps) { final Artifact artifact = d.getArtifact(); final String extension = artifact.getExtension(); final String artifactId = artifact.getArtifactId(); if ("json".equals(extension) && artifactId.endsWith(BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX)) { - descriptorKeys.add(new AppArtifactKey(artifact.getGroupId(), - artifactId.substring(0, - artifactId.length() - BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX.length()), - artifact.getVersion())); + platformReleases.addPlatformDescriptor(artifact.getGroupId(), artifactId, artifact.getClassifier(), extension, + artifact.getVersion()); } else if ("properties".equals(artifact.getExtension()) && artifactId.endsWith(BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX)) { - final Path propsPath = mvn.resolve(artifact).getArtifact().getFile().toPath(); - final Properties props = new Properties(); - try (InputStream is = Files.newInputStream(propsPath)) { - props.load(is); - } catch (IOException e) { - throw new AppModelResolverException("Failed to read properties from " + propsPath, e); - } - for (Map.Entry prop : props.entrySet()) { - final String name = String.valueOf(prop.getKey()); - if (name.startsWith(BootstrapConstants.PLATFORM_PROPERTY_PREFIX)) { - collectedProps.putIfAbsent(prop.getKey().toString(), prop.getValue().toString()); - } - } - propertyKeys.add(new AppArtifactKey(artifact.getGroupId(), - artifactId.substring(0, - artifactId.length() - BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX.length()), - artifact.getVersion())); - } - } - if (!descriptorKeys.containsAll(propertyKeys)) { - final StringBuilder buf = new StringBuilder(); - buf.append( - "The Quarkus platform properties applied to the project are missing the corresponding Quarkus platform BOM imports:"); - final int l = buf.length(); - for (AppArtifactKey key : propertyKeys) { - if (!descriptorKeys.contains(key)) { - if (l - buf.length() < 0) { - buf.append(','); - } - buf.append(' ').append(key); - } + platformReleases.addPlatformProperties(artifact.getGroupId(), artifactId, artifact.getClassifier(), extension, + artifact.getVersion(), mvn.resolve(artifact).getArtifact().getFile().toPath()); } - throw new AppModelResolverException(buf.toString()); } - appBuilder.addPlatformProperties(collectedProps); + appBuilder.setPlatformImports(platformReleases); } @Override diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute index 942a94c159849..e965fdf79cd14 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/build-layout.include.qute @@ -23,6 +23,9 @@ val quarkusPlatformVersion: String by project dependencies { implementation(enforcedPlatform("$\{quarkusPlatformGroupId}:$\{quarkusPlatformArtifactId}:$\{quarkusPlatformVersion}")) +{#for bom in boms} + implementation(enforcedPlatform("{bom}")) +{/for} {#for dep in dependencies} implementation("{dep}") {/for} diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/build-layout.include.qute b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/build-layout.include.qute index 85150c1e563c3..85646de02e1a5 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/build-layout.include.qute +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/build-layout.include.qute @@ -19,6 +19,9 @@ repositories { {#insert dependencies} dependencies { implementation enforcedPlatform("$\{quarkusPlatformGroupId}:$\{quarkusPlatformArtifactId}:$\{quarkusPlatformVersion}") +{#for bom in boms} + implementation enforcedPlatform('{bom}') +{/for} {#for dep in dependencies} implementation '{dep}' {/for} diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml index 565612ea9b3ce..5966fa76c1fd2 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml @@ -32,6 +32,15 @@ pom import + {#each boms} + + {it.groupId} + {it.artifactId} + {it.version} + pom + import + + {/each} {#if maven.repositories} diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInput.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInput.java index 957e672f18881..f1ce7a62fc8fa 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInput.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInput.java @@ -9,12 +9,14 @@ public class CodestartProjectInput { private final Collection dependencies; + private final Collection boms; private final Map data; private final CodestartsSelection selection; private final MessageWriter messageWriter; protected CodestartProjectInput(final CodestartProjectInputBuilder builder) { this.dependencies = requireNonNull(builder.dependencies, "dependencies is required"); + this.boms = requireNonNull(builder.boms, "boms is required"); this.selection = requireNonNull(builder.selection, "selection is required"); this.data = NestedMaps.unflatten(requireNonNull(builder.data, "data is required")); this.messageWriter = requireNonNull(builder.messageWriter, "messageWriter is required"); @@ -36,6 +38,10 @@ public Collection getDependencies() { return dependencies; } + public Collection getBoms() { + return boms; + } + public Map getData() { return data; } diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInputBuilder.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInputBuilder.java index 3045b761c010d..0dea8726973a3 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInputBuilder.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/CodestartProjectInputBuilder.java @@ -9,6 +9,7 @@ public class CodestartProjectInputBuilder { Collection dependencies = new ArrayList<>(); + Collection boms = new ArrayList<>(); CodestartsSelection selection = new CodestartsSelection(); Map data = new HashMap<>(); MessageWriter messageWriter = MessageWriter.info(); @@ -26,6 +27,11 @@ public CodestartProjectInputBuilder addDependency(String dependency) { return this.addDependencies(Collections.singletonList(dependency)); } + public CodestartProjectInputBuilder addBoms(Collection boms) { + this.boms.addAll(boms); + return this; + } + public CodestartProjectInputBuilder addCodestarts(Collection codestarts) { this.selection.addNames(codestarts); return this; diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/CodestartData.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/CodestartData.java index 1936afe2f28d2..0c54bb9c66d67 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/CodestartData.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/CodestartData.java @@ -54,8 +54,11 @@ public static Map buildCodestartProjectData(Collection buildDependenciesData(Stream codestartsStream, String languageName, - Collection extensions) { + Collection extensions, Collection platforms) { final Map> depsData = new HashMap<>(); + final Set boms = platforms.stream() + .map(CodestartDep::new) + .collect(Collectors.toCollection(LinkedHashSet::new)); final Set dependencies = extensions.stream() .map(CodestartDep::new) .collect(Collectors.toCollection(LinkedHashSet::new)); @@ -67,6 +70,7 @@ public static Map buildDependenciesData(Stream codest testDependencies.addAll(d.getTestDependencies()); }); depsData.put("dependencies", dependencies); + depsData.put("boms", boms); depsData.put("test-dependencies", testDependencies); return Collections.unmodifiableMap(depsData); } diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/DefaultCodestartProjectDefinition.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/DefaultCodestartProjectDefinition.java index 97815c134cb57..faa1914925ce9 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/DefaultCodestartProjectDefinition.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/DefaultCodestartProjectDefinition.java @@ -91,7 +91,7 @@ public Map getSharedData() { @Override public Map getDepsData() { return buildDependenciesData(getCodestarts().stream(), getLanguageName(), - getProjectInput().getDependencies()); + getProjectInput().getDependencies(), getProjectInput().getBoms()); } @Override diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInput.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInput.java index 90f54683aea72..8326b9870dbab 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInput.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInput.java @@ -12,12 +12,14 @@ public final class QuarkusCodestartProjectInput extends CodestartProjectInput { private final BuildTool buildTool; private final Collection extensions; + private final Collection platforms; private final String example; private Set appContent; public QuarkusCodestartProjectInput(QuarkusCodestartProjectInputBuilder builder) { super(builder); this.extensions = builder.extensions; + this.platforms = builder.platforms; this.example = builder.example; this.buildTool = requireNonNull(builder.buildTool, "buildTool is required"); this.appContent = builder.appContent; @@ -31,6 +33,10 @@ public Collection getExtensions() { return extensions; } + public Collection getPlatforms() { + return platforms; + } + public String getExample() { return example; } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInputBuilder.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInputBuilder.java index 00ca78c79c20c..898e31c658b66 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInputBuilder.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartProjectInputBuilder.java @@ -22,6 +22,7 @@ public class QuarkusCodestartProjectInputBuilder extends CodestartProjectInputBu private static final List FULL_CONTENT = Arrays.asList(AppContent.values()); Collection extensions = new ArrayList<>(); + Collection platforms = new ArrayList<>(); Set appContent = new HashSet<>(FULL_CONTENT); String example; BuildTool buildTool = BuildTool.MAVEN; @@ -44,6 +45,12 @@ public QuarkusCodestartProjectInputBuilder addExtension(ArtifactKey extension) { return this.addExtension(Extensions.toCoords(extension, null)); } + public QuarkusCodestartProjectInputBuilder addPlatforms(Collection boms) { + this.platforms.addAll(boms); + super.addBoms(boms.stream().map(Extensions::toGAV).collect(Collectors.toList())); + return this; + } + public QuarkusCodestartProjectInputBuilder example(String example) { this.example = example; return this; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java index c5e4775ab559a..a0b42a2df9b35 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java @@ -27,10 +27,14 @@ import io.quarkus.devtools.project.codegen.ProjectGenerator; import io.quarkus.maven.ArtifactCoords; import io.quarkus.platform.tools.ToolsUtils; +import io.quarkus.registry.catalog.Extension; import io.quarkus.registry.catalog.ExtensionCatalog; +import io.quarkus.registry.union.ElementCatalog; +import io.quarkus.registry.union.ElementCatalogBuilder; import java.io.IOException; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; @@ -45,18 +49,10 @@ public class CreateProjectCommandHandler implements QuarkusCommandHandler { @Override public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final ExtensionCatalog platformDescr = invocation.getExtensionsCatalog(); - final ArtifactCoords bom = platformDescr.getBom(); - if (bom == null) { - throw new QuarkusCommandException("The platform BOM is missing"); - } - invocation.setValue(BOM_GROUP_ID, bom.getGroupId()); - invocation.setValue(BOM_ARTIFACT_ID, bom.getArtifactId()); - invocation.setValue(BOM_VERSION, bom.getVersion()); - invocation.setValue(QUARKUS_VERSION, platformDescr.getQuarkusCoreVersion()); + final ExtensionCatalog extensionCatalog = invocation.getExtensionsCatalog(); final Set extensionsQuery = invocation.getValue(ProjectGenerator.EXTENSIONS, Collections.emptySet()); - final Properties quarkusProps = ToolsUtils.readQuarkusProperties(platformDescr); + final Properties quarkusProps = ToolsUtils.readQuarkusProperties(extensionCatalog); quarkusProps.forEach((k, v) -> { String name = k.toString().replace("-", "_"); if (!invocation.hasValue(name)) { @@ -82,15 +78,44 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws if (extensionsToAdd == null) { throw new QuarkusCommandException("Failed to create project because of invalid extensions"); } + + ArtifactCoords bom = null; + final List boms = getExtensionBoms(extensionCatalog, extensionsToAdd); + if (boms.isEmpty()) { + bom = extensionCatalog.getBom(); + } else { + final Iterator i = boms.iterator(); + while (i.hasNext()) { + final ArtifactCoords next = i.next(); + // TODO we remove quarkus-bom here because it's currently added by default using properties in the template + // ideally, it shouldn't be different than the rest of the imported BOMs + if (next.getArtifactId().equals("quarkus-bom")) { + bom = next; + i.remove(); + break; + } + } + } + + if (bom == null) { + throw new QuarkusCommandException("The platform BOM is missing"); + } + + invocation.setValue(BOM_GROUP_ID, bom.getGroupId()); + invocation.setValue(BOM_ARTIFACT_ID, bom.getArtifactId()); + invocation.setValue(BOM_VERSION, bom.getVersion()); + invocation.setValue(QUARKUS_VERSION, extensionCatalog.getQuarkusCoreVersion()); + try { Map platformData = new HashMap<>(); - if (platformDescr.getMetadata().get("maven") != null) { - platformData.put("maven", platformDescr.getMetadata().get("maven")); + if (extensionCatalog.getMetadata().get("maven") != null) { + platformData.put("maven", extensionCatalog.getMetadata().get("maven")); } - if (platformDescr.getMetadata().get("gradle") != null) { - platformData.put("gradle", platformDescr.getMetadata().get("gradle")); + if (extensionCatalog.getMetadata().get("gradle") != null) { + platformData.put("gradle", extensionCatalog.getMetadata().get("gradle")); } final QuarkusCodestartProjectInput input = QuarkusCodestartProjectInput.builder() + .addPlatforms(boms) .addExtensions(extensionsToAdd) .buildTool(invocation.getQuarkusProject().getBuildTool()) .example(invocation.getValue(EXAMPLE)) @@ -124,6 +149,32 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws } catch (IOException e) { throw new QuarkusCommandException("Failed to create project: " + e.getMessage(), e); } + return QuarkusCommandOutcome.success(); } + + private List getExtensionBoms(ExtensionCatalog extensionCatalog, List extensionsToAdd) + throws QuarkusCommandException { + final ElementCatalog ec = (ElementCatalog) extensionCatalog.getMetadata().get("element-catalog"); + if (ec == null) { + return Collections.emptyList(); + } + // we add quarkus-core as a selected extension here only to include the quarkus-bom + // in the list of platforms. quarkus-core won't be added to the generated POM though. + final Extension quarkusCore = extensionCatalog.getExtensions().stream() + .filter(e -> e.getArtifact().getArtifactId().equals("quarkus-core")).findFirst().get(); + if (quarkusCore == null) { + throw new QuarkusCommandException("Failed to locate quarkus-core in the extension catalog"); + } + final List eKeys; + if (extensionsToAdd.isEmpty()) { + eKeys = Collections.singletonList( + quarkusCore.getArtifact().getGroupId() + ":" + quarkusCore.getArtifact().getArtifactId()); + } else { + eKeys = extensionsToAdd.stream().map(e -> e.getGroupId() + ":" + e.getArtifactId()) + .collect(Collectors.toList()); + eKeys.add(quarkusCore.getArtifact().getGroupId() + ":" + quarkusCore.getArtifact().getArtifactId()); + } + return ElementCatalogBuilder.getBoms(ec, eKeys); + } } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java index 244e6a60540ac..ced8e3cdf1f2e 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java @@ -64,10 +64,13 @@ public static QuarkusProject getProject(Path projectDir, BuildTool buildTool, St public static ExtensionCatalog getExtensionCatalog(String quarkusVersion) { // TODO remove this method once the default registry becomes available try { - return registryClientEnabled && getCatalogResolver().hasRegistries() - ? getCatalogResolver().resolveExtensionCatalog(quarkusVersion) - : ToolsUtils.resolvePlatformDescriptorDirectly(null, null, quarkusVersion, artifactResolver(), - messageWriter()); + if (registryClientEnabled && getCatalogResolver().hasRegistries()) { + return quarkusVersion == null ? catalogResolver.resolveExtensionCatalog() + : catalogResolver.resolveExtensionCatalog(quarkusVersion); + } else { + return ToolsUtils.resolvePlatformDescriptorDirectly(null, null, quarkusVersion, artifactResolver(), + messageWriter()); + } } catch (Exception e) { throw new RuntimeException("Failed to resolve the Quarkus extension catalog", e); } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGroovyGradleBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGroovyGradleBuildFile.java index aa891c0f3fc8f..17fbb6ff1ffd8 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGroovyGradleBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGroovyGradleBuildFile.java @@ -29,6 +29,11 @@ String getBuildGradlePath() { return BUILD_GRADLE_PATH; } + @Override + protected boolean importBom(ArtifactCoords coords) { + return importBomInModel(getModel(), coords); + } + @Override protected boolean addDependency(ArtifactCoords coords, boolean managed) { return addDependencyInModel(getModel(), coords, managed); @@ -39,6 +44,12 @@ public BuildTool getBuildTool() { return BuildTool.GRADLE; } + static boolean importBomInModel(Model model, ArtifactCoords coords) { + return addDependencyInModel(model, + String.format(" implementation enforcedPlatform(%s)%n", + createDependencyCoordinatesString(coords, false, '\''))); + } + static boolean addDependencyInModel(Model model, ArtifactCoords coords, boolean managed) { return addDependencyInModel(model, String.format(" implementation %s%n", createDependencyCoordinatesString(coords, managed, '\''))); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java index 019692b474be7..c950a6c869142 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java @@ -53,7 +53,7 @@ public InstallResult install(ExtensionInstallPlan plan) throws IOException { List installedPlatforms = new ArrayList<>(); final Set alreadyInstalled = alreadyInstalled(plan.toCollection()); for (ArtifactCoords platform : withoutAlreadyInstalled(alreadyInstalled, plan.getPlatforms())) { - if (addDependency(platform, false)) { + if (importBom(platform)) { installedPlatforms.add(platform); } } @@ -116,6 +116,8 @@ private Collection withoutAlreadyInstalled(Set exis .collect(toList()); } + protected abstract boolean importBom(ArtifactCoords coords); + protected abstract boolean addDependency(ArtifactCoords coords, boolean managed); protected abstract void removeDependency(ArtifactKey key) throws IOException; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java index 6c5e33ac9ac82..358b2e2968d84 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java @@ -49,6 +49,32 @@ public void writeToDisk() throws IOException { } } + @Override + protected boolean importBom(ArtifactCoords coords) { + if (!"pom".equalsIgnoreCase(coords.getType())) { + throw new IllegalArgumentException(coords + " is not a POM"); + } + Model model = getModel(); + final Dependency d = new Dependency(); + d.setGroupId(coords.getGroupId()); + d.setArtifactId(coords.getArtifactId()); + d.setType(coords.getType()); + d.setScope("import"); + DependencyManagement dependencyManagement = model.getDependencyManagement(); + if (dependencyManagement == null) { + dependencyManagement = new DependencyManagement(); + model.setDependencyManagement(dependencyManagement); + } + if (dependencyManagement.getDependencies() + .stream() + .map(this::toResolvedDependency) + .noneMatch(thisDep -> d.getManagementKey().equals(thisDep.getManagementKey()))) { + dependencyManagement.addDependency(d); + return true; + } + return false; + } + @Override protected boolean addDependency(ArtifactCoords coords, boolean managed) { Model model = getModel(); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java index cc3d34f4c612a..3ac1daa0c004f 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java @@ -6,6 +6,7 @@ import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; +import io.quarkus.bootstrap.resolver.maven.workspace.ModelUtils; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; @@ -86,7 +87,8 @@ public static QuarkusProject getProject(Artifact projectPom, Model projectModel, : ExtensionCatalogResolver.empty(); if (catalogResolver.hasRegistries()) { try { - extensionCatalog = catalogResolver.resolveExtensionCatalog(quarkusVersion); + //extensionCatalog = catalogResolver.resolveExtensionCatalog(quarkusVersion); + extensionCatalog = catalogResolver.resolveExtensionCatalog(); } catch (RegistryResolutionException e) { throw new RuntimeException("Failed to resolve extension catalog", e); } @@ -167,9 +169,9 @@ private static boolean isSameFile(Path p1, Path p2) { } } - private final Model model; - private final List managedDependencies; - private final Properties projectProps; + private Model model; + private List managedDependencies; + private Properties projectProps; private Supplier> projectDepsSupplier; private List dependencies; private List importedPlatforms; @@ -190,6 +192,36 @@ public BuildTool getBuildTool() { return BuildTool.MAVEN; } + @Override + protected boolean importBom(ArtifactCoords coords) { + if (!"pom".equalsIgnoreCase(coords.getType())) { + throw new IllegalArgumentException(coords + " is not a POM"); + } + final Dependency d = new Dependency(); + d.setGroupId(coords.getGroupId()); + d.setArtifactId(coords.getArtifactId()); + d.setType(coords.getType()); + d.setScope("import"); + d.setVersion(coords.getVersion()); + DependencyManagement dependencyManagement = model().getDependencyManagement(); + if (dependencyManagement == null) { + dependencyManagement = new DependencyManagement(); + model().setDependencyManagement(dependencyManagement); + } + if (dependencyManagement.getDependencies() + .stream() + .filter(t -> t.getScope().equals("import")) + .noneMatch(thisDep -> d.getManagementKey().equals(resolveKey(thisDep)))) { + dependencyManagement.addDependency(d); + // the effective managed dependencies set may already include it + if (!getManagedDependencies().contains(coords)) { + getManagedDependencies().add(coords); + } + return true; + } + return false; + } + @Override protected boolean addDependency(ArtifactCoords coords, boolean managed) { final Dependency d = new Dependency(); @@ -299,6 +331,21 @@ protected String getProperty(String propertyName) { @Override protected void refreshData() { + final Path projectPom = getProjectDirPath().resolve("pom.xml"); + if (Files.exists(projectPom)) { + try { + model = ModelUtils.readModel(projectPom); + } catch (IOException e) { + throw new RuntimeException("Failed to read " + projectPom, e); + } + projectProps = model.getProperties(); + final ArtifactDescriptorResult descriptor = describe(getMavenResolver(getProjectDirPath()), new DefaultArtifact( + ModelUtils.getGroupId(model), model.getArtifactId(), "pom", ModelUtils.getVersion(model))); + managedDependencies = toArtifactCoords(descriptor.getManagedDependencies()); + dependencies = null; + projectDepsSupplier = () -> toArtifactCoords(descriptor.getDependencies()); + + } } private int getIndexToAddExtension() { diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java index 09d4d672482a5..e3ac2da89374a 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java @@ -17,6 +17,11 @@ import io.quarkus.registry.config.RegistriesConfig; import io.quarkus.registry.config.RegistriesConfigLocator; import io.quarkus.registry.config.RegistryConfig; +import io.quarkus.registry.union.ElementCatalog; +import io.quarkus.registry.union.ElementCatalogBuilder; +import io.quarkus.registry.union.ElementCatalogBuilder.MemberBuilder; +import io.quarkus.registry.union.ElementCatalogBuilder.UnionBuilder; +import io.quarkus.registry.util.PlatformArtifacts; import java.io.File; import java.net.URL; import java.net.URLClassLoader; @@ -250,8 +255,75 @@ private void collectPlatforms(PlatformCatalog catalog, List collectedP } } + @SuppressWarnings("unchecked") public ExtensionCatalog resolveExtensionCatalog() throws RegistryResolutionException { - return resolveExtensionCatalog((String) null); + + final int registriesTotal = registries.size(); + if (registriesTotal == 0) { + throw new RegistryResolutionException("No registries configured"); + } + + final Set processedUnions = new HashSet<>(); + final List psList = new ArrayList<>(); + final Map platformDescrMap = new HashMap<>(); + final List catalogs = new ArrayList<>(); + final ElementCatalogBuilder catalogBuilder = ElementCatalogBuilder.newInstance(); + + for (RegistryExtensionResolver registry : registries) { + final PlatformCatalog pc = registry.resolvePlatformCatalog(); + if (pc == null) { + continue; + } + for (Platform p : pc.getPlatforms()) { + final ExtensionCatalog ec = registry.resolvePlatformExtensions(p.getBom()); + catalogs.add(ec); + platformDescrMap.put(ec.getBom().getGroupId() + ":" + ec.getBom().getArtifactId(), ec); + + final Map platformRelease = (Map) ec.getMetadata().get("platform-release"); + if (platformRelease != null) { + final String versionStr = (String) platformRelease.get("version"); + if (!processedUnions.add(versionStr)) { + continue; + } + final UnionBuilder union = catalogBuilder.getOrCreateUnion(Integer.parseInt(versionStr)); + psList.add(new ParsedPlatformStack(union, ec.getId(), (List) platformRelease.get("members"))); + addMember(union, ec); + } + } + } + + for (ParsedPlatformStack stack : psList) { + for (String memberCoordsStr : stack.members) { + if (stack.originMemberId.equals(memberCoordsStr)) { + continue; + } + final ArtifactCoords memberCoords = ArtifactCoords.fromString(memberCoordsStr); + ExtensionCatalog memberCatalog = platformDescrMap + .get(memberCoords.getGroupId() + ":" + + PlatformArtifacts.ensureBomArtifactId(memberCoords.getArtifactId())); + if (memberCatalog == null || !memberCatalog.getBom().getVersion().equals(memberCoords.getVersion())) { + memberCatalog = registries.get(0).resolvePlatformExtensions(memberCoords); + } + + if (memberCatalog != null) { + addMember(stack.unionBuilder, memberCatalog); + } + } + } + + final ExtensionCatalog catalog = JsonCatalogMerger.merge(catalogs); + final ElementCatalog elements = catalogBuilder.build(); + if (!elements.isEmpty()) { + catalog.getMetadata().put("element-catalog", elements); + } + return catalog; + } + + private static void addMember(final UnionBuilder union, ExtensionCatalog member) { + final MemberBuilder builder = union.getOrCreateMember( + member.getBom().getGroupId() + ":" + member.getBom().getArtifactId(), member.getBom().getVersion()); + member.getExtensions() + .forEach(e -> builder.addElement(e.getArtifact().getGroupId() + ":" + e.getArtifact().getArtifactId())); } public ExtensionCatalog resolveExtensionCatalog(String quarkusCoreVersion) throws RegistryResolutionException { @@ -499,4 +571,16 @@ private List filterRegistries(Function members; + + public ParsedPlatformStack(UnionBuilder ub, String originMemberId, List members) { + this.unionBuilder = ub; + this.originMemberId = originMemberId; + this.members = members; + } + } } diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Element.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Element.java new file mode 100644 index 0000000000000..b709dac8b9483 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Element.java @@ -0,0 +1,20 @@ +package io.quarkus.registry.union; + +import java.util.Collection; + +public interface Element { + + /** + * Element key. + * + * @return element key + */ + Object key(); + + /** + * Members that provide the element. + * + * @return members that provide the element + */ + Collection members(); +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalog.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalog.java new file mode 100644 index 0000000000000..442ed6fcbf7a7 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalog.java @@ -0,0 +1,35 @@ +package io.quarkus.registry.union; + +import java.util.Collection; + +public interface ElementCatalog { + + /** + * All elements of the catalog + * + * @return elements of the catalog + */ + Collection elements(); + + /** + * All element keys + * + * @return all element keys + */ + Collection elementKeys(); + + /** + * Returns an element for a given key. + * + * @param elementKey element key + * @return element associated with the key or null + */ + Element get(Object elementKey); + + /** + * Checks whether the catalog contains any elements. + * + * @return true if the catalog does not contain any elements, otherwise - false + */ + boolean isEmpty(); +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalogBuilder.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalogBuilder.java new file mode 100644 index 0000000000000..56ebda35b87ef --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/ElementCatalogBuilder.java @@ -0,0 +1,361 @@ +package io.quarkus.registry.union; + +import io.quarkus.maven.ArtifactCoords; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +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.Set; +import java.util.TreeMap; + +public class ElementCatalogBuilder { + + public static ElementCatalogBuilder newInstance() { + return new ElementCatalogBuilder(); + } + + public class ElementBuilder extends BuildCallback { + + private final Object key; + private final Object version; + private final List> callbacks = new ArrayList<>(4); + private final List members = new ArrayList<>(); + + private ElementBuilder(Object key, Object version) { + this.key = Objects.requireNonNull(key); + this.version = Objects.requireNonNull(version); + } + + private ElementBuilder addCallback(MemberBuilder callback) { + callbacks.add(callback); + callback.callbacks.add(this); + return this; + } + + private Element build() { + final Element e = new Element() { + @Override + public Object key() { + return key; + } + + @Override + public Collection members() { + return members; + } + + @Override + public String toString() { + return key.toString() + "#" + version; + } + }; + callbacks.forEach(c -> c.created(e)); + return e; + } + + @Override + protected void created(Member t) { + members.add(t); + } + } + + public class MemberBuilder extends BuildCallback { + private final Object key; + private final Object version; + private Union initialUnion; + private List unionVersions = new ArrayList<>(); + private final List> callbacks = new ArrayList<>(); + private final Map elements = new HashMap<>(); + + private MemberBuilder(Object key, Object version) { + this.key = Objects.requireNonNull(key); + this.version = Objects.requireNonNull(version); + } + + public ElementBuilder addElement(Object elementKey) { + return getOrCreateElement(elementKey, version).addCallback(this); + } + + MemberBuilder addUnion(UnionBuilder union) { + callbacks.add(union); + unionVersions.add(union.version); + return this; + } + + @Override + protected void created(Element t) { + elements.put(t.key(), t); + } + + public Member build() { + final Member m = new Member() { + + @Override + public Object key() { + return key; + } + + @Override + public Object version() { + return version; + } + + @Override + public Union initialUnion() { + return initialUnion; + } + + @Override + public Collection elements() { + return elements.values(); + } + + @Override + public Collection elementKeys() { + return elements.keySet(); + } + + @Override + public Element get(Object elementKey) { + return elements.get(elementKey); + } + + @Override + public String toString() { + return key.toString() + "#" + version + elements.values(); + } + + @Override + public boolean containsAll(Collection elementKeys) { + return elements.keySet().containsAll(elementKeys); + } + + @Override + public Collection unions() { + return unionVersions; + } + + @Override + public boolean isEmpty() { + return elements.isEmpty(); + } + }; + callbacks.forEach(c -> c.created(m)); + return m; + } + } + + public class UnionBuilder extends BuildCallback { + + private final UnionVersion version; + private final List memberBuilders = new ArrayList<>(); + private final Map members = new HashMap<>(); + + private UnionBuilder(UnionVersion version) { + this.version = Objects.requireNonNull(version); + } + + public MemberBuilder getOrCreateMember(Object memberKey, Object memberVersion) { + final MemberBuilder mb = ElementCatalogBuilder.this.getOrCreateMember(memberKey, memberVersion); + memberBuilders.add(mb); + return mb.addUnion(this); + } + + @Override + protected void created(Member t) { + members.put(t.key(), t); + } + + public Union build() { + final Union u = new Union() { + + @Override + public UnionVersion verion() { + return version; + } + + @Override + public Collection members() { + return members.values(); + } + + @Override + public Member member(Object memberKey) { + return members.get(memberKey); + } + + @Override + public String toString() { + return version.toString() + members; + } + }; + for (MemberBuilder mb : memberBuilders) { + mb.initialUnion = u; + } + return u; + } + } + + private abstract class BuildCallback { + + protected abstract void created(T t); + } + + static class IntVersion implements UnionVersion { + + static UnionVersion get(Integer i) { + return new IntVersion(i); + } + + private final Integer version; + + public IntVersion(int version) { + this.version = version; + } + + @Override + public int compareTo(UnionVersion o) { + if (o instanceof IntVersion) { + return version.compareTo(((IntVersion) o).version); + } + throw new IllegalArgumentException(o + " is not an instance of " + IntVersion.class.getName()); + } + + @Override + public String toString() { + return version.toString(); + } + } + + private final Map elements = new HashMap<>(); + private final Map members = new HashMap<>(); + private final Map unions = new HashMap<>(); + + private ElementBuilder getOrCreateElement(Object elementKey, Object elementVersion) { + return elements.computeIfAbsent(elementKey, k -> new ElementBuilder(k, elementVersion)); + } + + private MemberBuilder getOrCreateMember(Object key, Object version) { + return members.computeIfAbsent(key + ":" + version, k -> new MemberBuilder(key, version)); + } + + public UnionBuilder getOrCreateUnion(int version) { + return getOrCreateUnion(IntVersion.get(version)); + } + + public UnionBuilder getOrCreateUnion(UnionVersion version) { + return unions.computeIfAbsent(version, v -> new UnionBuilder(version)); + } + + public ElementCatalog build() { + + final Map map = new HashMap<>(elements.size()); + for (ElementBuilder eb : elements.values()) { + final Element e = eb.build(); + map.put(e.key(), e); + } + for (MemberBuilder m : members.values()) { + m.build(); + } + for (UnionBuilder u : unions.values()) { + u.build(); + } + + final ElementCatalog catalog = new ElementCatalog() { + + @Override + public Collection elements() { + return map.values(); + } + + @Override + public Collection elementKeys() { + return elements.keySet(); + } + + @Override + public Element get(Object elementKey) { + return map.get(elementKey); + } + + @Override + public String toString() { + return elements.toString(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + }; + + return catalog; + + } + + public static void dump(PrintStream ps, ElementCatalog catalog) { + ps.println("Element Catalog:"); + final Map> unions = new TreeMap<>(); + for (Element e : catalog.elements()) { + for (Member m : e.members()) { + for (UnionVersion uv : m.unions()) { + unions.computeIfAbsent(uv, v -> new HashMap<>()).put(m.key(), m); + } + } + } + for (Map.Entry> entry : unions.entrySet()) { + System.out.println("Union " + entry.getKey()); + for (Member m : entry.getValue().values()) { + System.out.println(" Member " + m.key() + ":" + m.version()); + for (Object e : m.elementKeys()) { + System.out.println(" Element " + e); + } + } + } + } + + public static List getBoms(ElementCatalog elementCatalog, Collection elementKeys) { + + final Comparator comparator = UnionVersion::compareTo; + final Map> unionVersions = new TreeMap<>(comparator.reversed()); + for (Object elementKey : elementKeys) { + final Element e = elementCatalog.get(elementKey); + if (e == null) { + throw new RuntimeException( + "Element " + elementKey + " not found in the catalog " + elementCatalog.elementKeys()); + } + for (Member m : e.members()) { + for (UnionVersion uv : m.unions()) { + unionVersions.computeIfAbsent(uv, v -> new HashMap<>()) + .put(ArtifactCoords.fromString(m.key() + "::pom:" + m.version()), m); + } + } + } + + for (Map members : unionVersions.values()) { + final Set memberElementKeys = new HashSet<>(); + final Iterator i = members.values().iterator(); + Member m = null; + while (i.hasNext()) { + m = i.next(); + memberElementKeys.addAll(m.elementKeys()); + } + if (memberElementKeys.containsAll(elementKeys)) { + final List boms = new ArrayList<>(); + for (ArtifactCoords bom : members.keySet()) { + boms.add(bom); + } + return boms; + } + } + return Collections.emptyList(); + } + +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Member.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Member.java new file mode 100644 index 0000000000000..dd9d9da21ae41 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Member.java @@ -0,0 +1,42 @@ +package io.quarkus.registry.union; + +import java.util.Collection; + +public interface Member extends ElementCatalog { + + /** + * Member key + * + * @return member key + */ + Object key(); + + /** + * Member version + * + * @return member version + */ + Object version(); + + /** + * The very first union the member joined. + * + * @return the very first union the member joined the union + */ + Union initialUnion(); + + /** + * Unions this member belongs to. + * + * @return unions this member belongs to + */ + Collection unions(); + + /** + * Checks whether this member contains all the element keys. + * + * @param elementKeys element keys + * @return true if the member contains all the element keys, otherwise - false + */ + boolean containsAll(Collection elementKeys); +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Union.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Union.java new file mode 100644 index 0000000000000..2b783dc6f4a91 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/Union.java @@ -0,0 +1,29 @@ +package io.quarkus.registry.union; + +import java.util.Collection; + +public interface Union { + + /** + * Union version. + * + * @return union version + */ + UnionVersion verion(); + + /** + * Members of the union. + * + * @return members of the union + */ + Collection members(); + + /** + * Returns a union member associated with a key or null, in case + * the union does not contain a member associated with the key. + * + * @param memberKey member key + * @return member corresponding to the key or null, in case the member with the key was not found in the union + */ + Member member(Object memberKey); +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/UnionVersion.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/UnionVersion.java new file mode 100644 index 0000000000000..aa502d1a34a55 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/union/UnionVersion.java @@ -0,0 +1,5 @@ +package io.quarkus.registry.union; + +public interface UnionVersion extends Comparable { + +} diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java index 251bcb0baaac3..b897bc4e3fcc9 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java @@ -22,11 +22,11 @@ import org.gradle.api.artifacts.ResolvedModuleVersion; import org.junit.jupiter.api.Test; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.SourceSet; +import io.quarkus.bootstrap.model.gradle.Workspace; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import io.quarkus.bootstrap.resolver.QuarkusGradleModelFactory; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.SourceSet; -import io.quarkus.bootstrap.resolver.model.Workspace; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; class QuarkusModelBuilderTest { diff --git a/test-framework/jacoco/deployment/src/main/java/io/quarkus/jacoco/deployment/JacocoProcessor.java b/test-framework/jacoco/deployment/src/main/java/io/quarkus/jacoco/deployment/JacocoProcessor.java index e040fce63c2f5..542bd12ef094e 100644 --- a/test-framework/jacoco/deployment/src/main/java/io/quarkus/jacoco/deployment/JacocoProcessor.java +++ b/test-framework/jacoco/deployment/src/main/java/io/quarkus/jacoco/deployment/JacocoProcessor.java @@ -18,9 +18,9 @@ import io.quarkus.bootstrap.model.AppArtifactKey; import io.quarkus.bootstrap.model.AppDependency; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.WorkspaceModule; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; -import io.quarkus.bootstrap.resolver.model.WorkspaceModule; import io.quarkus.bootstrap.utils.BuildToolHelper; import io.quarkus.deployment.ApplicationArchive; import io.quarkus.deployment.IsTest; diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java index e5f535d0ec2cc..b66ec31a131b7 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java @@ -31,7 +31,7 @@ import io.quarkus.bootstrap.app.CuratedApplication; import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.model.PathsCollection; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import io.quarkus.bootstrap.utils.BuildToolHelper; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem; import io.quarkus.runtime.configuration.ProfileManager; diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index b9b04a6c6abcb..a1ec93a87d9d6 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -76,7 +76,7 @@ import io.quarkus.bootstrap.classloading.ClassPathElement; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.bootstrap.model.PathsCollection; -import io.quarkus.bootstrap.resolver.model.QuarkusModel; +import io.quarkus.bootstrap.model.gradle.QuarkusModel; import io.quarkus.bootstrap.runner.Timing; import io.quarkus.bootstrap.utils.BuildToolHelper; import io.quarkus.builder.BuildChainBuilder;