From abcd3753eaaab77cb5fc3e951b2a60667cecd508 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 9 Dec 2022 00:18:42 +0100 Subject: [PATCH] Propage parent-first flag --- .../ParentFirstFlagPropagationTest.java | 77 ++++++++++++ .../RunnerParentFirstFlagPropagationTest.java | 76 ++++++++++++ .../bootstrap/resolver/TsQuarkusExt.java | 30 ++++- .../ApplicationDependencyTreeResolver.java | 116 +++++++++++++++--- 4 files changed, 280 insertions(+), 19 deletions(-) create mode 100644 core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ParentFirstFlagPropagationTest.java create mode 100644 core/deployment/src/test/java/io/quarkus/deployment/runnerjar/RunnerParentFirstFlagPropagationTest.java diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ParentFirstFlagPropagationTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ParentFirstFlagPropagationTest.java new file mode 100644 index 0000000000000..9f992898e9e73 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ParentFirstFlagPropagationTest.java @@ -0,0 +1,77 @@ +package io.quarkus.deployment.runnerjar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactDependency; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.DependencyFlags; + +public class ParentFirstFlagPropagationTest extends BootstrapFromOriginalJarTestBase { + + @Override + protected TsArtifact composeApplication() { + + final TsArtifact extADep1 = TsArtifact.jar("ext-a-dep"); + addToExpectedLib(extADep1); + final TsArtifact extBDep1 = TsArtifact.jar("ext-b-dep"); + addToExpectedLib(extBDep1); + final TsArtifact extBDepTrans1 = TsArtifact.jar("ext-b-dep-trans"); + addToExpectedLib(extBDepTrans1); + extBDep1.addDependency(extBDepTrans1); + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + extA.getRuntime() + .addDependency(extADep1) + .addDependency(extBDep1, extBDepTrans1); + addToExpectedLib(extA.getRuntime()); + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + addToExpectedLib(extB.getRuntime()); + extB.getRuntime().addDependency(extBDep1); + extB.addDependency(extA); + extB.setDependencyFlag(extBDep1.getKey(), DependencyFlags.CLASSLOADER_PARENT_FIRST); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extB); + } + + @Override + protected void assertAppModel(ApplicationModel appModel) throws Exception { + final Set expectedDeployDeps = Set.of( + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-deployment", "1"), "compile", + DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), "compile", + DependencyFlags.DEPLOYMENT_CP)); + assertEquals(expectedDeployDeps, appModel.getDependencies().stream().filter(d -> d.isDeploymentCp() && !d.isRuntimeCp()) + .map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + + final Set expectedRuntimeDeps = Set.of( + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a", "1"), "compile", + DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-dep", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b", "1"), "compile", + DependencyFlags.DIRECT, DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, DependencyFlags.RUNTIME_CP, + DependencyFlags.DEPLOYMENT_CP, DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-dep", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP, DependencyFlags.CLASSLOADER_PARENT_FIRST), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-dep-trans", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP, DependencyFlags.CLASSLOADER_PARENT_FIRST)); + assertEquals(expectedRuntimeDeps, + appModel.getRuntimeDependencies().stream().map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + final Set expectedFullDeps = new HashSet<>(); + expectedFullDeps.addAll(expectedDeployDeps); + expectedFullDeps.addAll(expectedRuntimeDeps); + assertEquals(expectedFullDeps, + appModel.getDependencies().stream().map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + } +} diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/RunnerParentFirstFlagPropagationTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/RunnerParentFirstFlagPropagationTest.java new file mode 100644 index 0000000000000..8b20d2c88aee7 --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/RunnerParentFirstFlagPropagationTest.java @@ -0,0 +1,76 @@ +package io.quarkus.deployment.runnerjar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactDependency; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.DependencyFlags; + +public class RunnerParentFirstFlagPropagationTest extends BootstrapFromOriginalJarTestBase { + @Override + protected TsArtifact composeApplication() { + + final TsArtifact extADep1 = TsArtifact.jar("ext-a-dep"); + addToExpectedLib(extADep1); + final TsArtifact extBDep1 = TsArtifact.jar("ext-b-dep"); + addToExpectedLib(extBDep1); + final TsArtifact extBDepTrans1 = TsArtifact.jar("ext-b-dep-trans"); + addToExpectedLib(extBDepTrans1); + extBDep1.addDependency(extBDepTrans1); + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + extA.getRuntime() + .addDependency(extADep1) + .addDependency(extBDep1, extBDepTrans1); + addToExpectedLib(extA.getRuntime()); + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + addToExpectedLib(extB.getRuntime()); + extB.getRuntime().addDependency(extBDep1); + extB.addDependency(extA); + extB.setDependencyFlag(extBDep1.getKey(), DependencyFlags.CLASSLOADER_PARENT_FIRST); + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(extB); + } + + @Override + protected void assertAppModel(ApplicationModel appModel) throws Exception { + final Set expectedDeployDeps = Set.of( + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-deployment", "1"), "compile", + DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), "compile", + DependencyFlags.DEPLOYMENT_CP)); + assertEquals(expectedDeployDeps, appModel.getDependencies().stream().filter(d -> d.isDeploymentCp() && !d.isRuntimeCp()) + .map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + + final Set expectedRuntimeDeps = Set.of( + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a", "1"), "compile", + DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-dep", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b", "1"), "compile", + DependencyFlags.DIRECT, DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, DependencyFlags.RUNTIME_CP, + DependencyFlags.DEPLOYMENT_CP, DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-dep", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP, DependencyFlags.CLASSLOADER_PARENT_FIRST), + new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-dep-trans", "1"), "compile", + DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP, DependencyFlags.CLASSLOADER_PARENT_FIRST)); + assertEquals(expectedRuntimeDeps, + appModel.getRuntimeDependencies().stream().map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + final Set expectedFullDeps = new HashSet<>(); + expectedFullDeps.addAll(expectedDeployDeps); + expectedFullDeps.addAll(expectedRuntimeDeps); + assertEquals(expectedFullDeps, + appModel.getDependencies().stream().map(d -> new ArtifactDependency(d)).collect(Collectors.toSet())); + } +} diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java index cce22dcf20460..29fef873ab273 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java @@ -2,11 +2,15 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; -import java.util.Properties; +import java.util.Map; +import java.util.StringJoiner; import java.util.stream.Collectors; import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.DependencyFlags; public class TsQuarkusExt { @@ -16,6 +20,7 @@ public class TsQuarkusExt { protected final TsJar rtContent; protected final PropsBuilder rtDescr = PropsBuilder.newInstance(); private boolean installed; + private Map> flags = new HashMap<>(); public TsQuarkusExt(String artifactId) { this(artifactId, TsArtifact.DEFAULT_VERSION); @@ -30,6 +35,11 @@ public TsQuarkusExt(String artifactId, String version) { rtDescr.set(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT, deployment.toString()); } + public TsQuarkusExt setDependencyFlag(ArtifactKey dep, int flag) { + this.flags.computeIfAbsent(flag, k -> new ArrayList<>()).add(dep); + return this; + } + public TsQuarkusExt setConditionalDeps(TsQuarkusExt... exts) { final StringBuilder buf = new StringBuilder(); int i = 0; @@ -83,15 +93,25 @@ public void install(TsRepoBuilder repo) { } installed = true; - rtContent.addEntry(rtDescr.build(), BootstrapConstants.DESCRIPTOR_PATH); - if (!extDeps.isEmpty()) { for (TsQuarkusExt e : extDeps) { e.install(repo); } } - Properties props = rtDescr.build(); - rtContent.addEntry(props, BootstrapConstants.DESCRIPTOR_PATH); + for (Map.Entry> e : flags.entrySet()) { + switch (e.getKey()) { + case DependencyFlags.CLASSLOADER_PARENT_FIRST: + final StringJoiner sj = new StringJoiner(","); + for (ArtifactKey k : e.getValue()) { + sj.add(k.toString()); + } + rtDescr.set(BootstrapConstants.PARENT_FIRST_ARTIFACTS, sj.toString()); + break; + default: + throw new RuntimeException("Not yet supported flag " + e.getKey()); + } + } + rtContent.addEntry(rtDescr.build(), BootstrapConstants.DESCRIPTOR_PATH); deployment.install(repo); runtime.install(repo); } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java index 21d10db1cec82..728cddf764235 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java @@ -54,6 +54,7 @@ import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.DependencyFlags; +import io.quarkus.maven.dependency.GACT; import io.quarkus.maven.dependency.ResolvedDependencyBuilder; import io.quarkus.paths.PathList; import io.quarkus.paths.PathTree; @@ -209,21 +210,42 @@ public void resolve(CollectRequest collectRtDepsRequest) throws AppModelResolver buildTreeConsumer); buildDepsVisitor.visit(root); - if (!CONVERGED_TREE_ONLY && collectReloadableModules) { - for (ResolvedDependencyBuilder db : appBuilder.getDependencies()) { - if (db.isFlagSet(DependencyFlags.RELOADABLE | DependencyFlags.VISITED)) { - continue; + for (ExtensionInfo e : allExtensions.values()) { + if (!e.activated) { + continue; + } + e.handleExtensionProperties(); + } + + if (!CONVERGED_TREE_ONLY) { + if (collectReloadableModules) { + for (ResolvedDependencyBuilder db : appBuilder.getDependencies()) { + if (!db.isFlagSet(DependencyFlags.RELOADABLE | DependencyFlags.VISITED)) { + propagateFlags(db, DependencyFlags.RELOADABLE, 0); + } } - clearReloadableFlag(db); + clearVisitedFlag(); } + propagateFlag(DependencyFlags.CLASSLOADER_PARENT_FIRST); + propagateFlag(DependencyFlags.CLASSLOADER_RUNNER_PARENT_FIRST); } + collectPlatformProperties(); + } + + private void propagateFlag(int flag) { for (ResolvedDependencyBuilder db : appBuilder.getDependencies()) { - db.clearFlag(DependencyFlags.VISITED); - appBuilder.addDependency(db); + if (db.isFlagSet(flag) && !db.isFlagSet(DependencyFlags.VISITED)) { + propagateFlags(db, 0, flag); + } } + clearVisitedFlag(); + } - collectPlatformProperties(); + private void clearVisitedFlag() { + for (ResolvedDependencyBuilder db : appBuilder.getDependencies()) { + db.clearFlag(DependencyFlags.VISITED); + } } private void collectPlatformProperties() throws AppModelResolverException { @@ -245,7 +267,7 @@ private void collectPlatformProperties() throws AppModelResolverException { appBuilder.setPlatformImports(platformReleases); } - private void clearReloadableFlag(ResolvedDependencyBuilder db) { + private void propagateFlags(ResolvedDependencyBuilder db, int toClear, int toSet) { final Set deps = artifactDeps.get(db.getArtifactCoords()); if (deps == null || deps.isEmpty()) { return; @@ -255,9 +277,9 @@ private void clearReloadableFlag(ResolvedDependencyBuilder db) { if (dep == null || dep.isFlagSet(DependencyFlags.VISITED)) { continue; } - dep.setFlags(DependencyFlags.VISITED); - dep.clearFlag(DependencyFlags.RELOADABLE); - clearReloadableFlag(dep); + dep.setFlags(DependencyFlags.VISITED | toSet); + dep.clearFlag(toClear); + propagateFlags(dep, toClear, toSet); } } @@ -673,13 +695,79 @@ private class ExtensionInfo { .parseDependencyCondition(props.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); } + public void handleExtensionProperties() { + for (Map.Entry prop : props.entrySet()) { + if (prop.getValue() == null) { + continue; + } + final String value = prop.getValue().toString(); + if (value.isBlank()) { + continue; + } + final String name = prop.getKey().toString(); + switch (name) { + case ApplicationModelBuilder.PARENT_FIRST_ARTIFACTS: + for (String artifact : value.split(",")) { + final ResolvedDependencyBuilder d = appBuilder.getDependency(new GACT(artifact.split(":"))); + if (d != null) { + d.setFlags(DependencyFlags.CLASSLOADER_PARENT_FIRST); + } + } + break; + case ApplicationModelBuilder.RUNNER_PARENT_FIRST_ARTIFACTS: + for (String artifact : value.split(",")) { + final ResolvedDependencyBuilder d = appBuilder.getDependency(new GACT(artifact.split(":"))); + if (d != null) { + d.setFlags(DependencyFlags.CLASSLOADER_RUNNER_PARENT_FIRST); + } + } + break; + case ApplicationModelBuilder.LESSER_PRIORITY_ARTIFACTS: + String[] artifacts = value.split(","); + for (String artifact : artifacts) { + final ResolvedDependencyBuilder d = appBuilder.getDependency(new GACT(artifact.split(":"))); + if (d != null) { + log.debugf("Extension %s is making %s a lesser priority artifact", runtimeArtifact, artifact); + d.setFlags(DependencyFlags.CLASSLOADER_LESSER_PRIORITY); + } + } + break; + case ApplicationModelBuilder.EXCLUDED_ARTIFACTS: + for (String artifact : value.split(",")) { + appBuilder.addExcludedArtifact(new GACT(artifact.split(":"))); + log.debugf("Extension %s is excluding %s", runtimeArtifact, artifact); + } + break; + default: + if (name.startsWith(ApplicationModelBuilder.REMOVED_RESOURCES_DOT)) { + final String keyStr = name.substring(ApplicationModelBuilder.REMOVED_RESOURCES_DOT.length()); + if (!keyStr.isBlank()) { + ArtifactKey key = null; + try { + key = ArtifactKey.fromString(keyStr); + } catch (IllegalArgumentException e) { + log.warnf("Failed to parse artifact key %s in %s from descriptor of extension %s", keyStr, + name, + runtimeArtifact); + } + if (key != null) { + final Set resources = Set.of(value.split(",")); + appBuilder.addRemovedResources(key, resources); + log.debugf("Extension %s is excluding resources %s from artifact %s", runtimeArtifact, + resources, + key); + } + } + } + } + } + } + void ensureActivated() { if (activated) { return; } activated = true; - appBuilder.handleExtensionProperties(props, runtimeArtifact.toString()); - final String providesCapabilities = props.getProperty(BootstrapConstants.PROP_PROVIDES_CAPABILITIES); final String requiresCapabilities = props.getProperty(BootstrapConstants.PROP_REQUIRES_CAPABILITIES); if (providesCapabilities != null || requiresCapabilities != null) {