Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
import org.eclipse.aether.util.graph.transformer.ConflictMarker;
import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
import org.eclipse.aether.version.Version;

import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppArtifactKey;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.maven.BuildDependencyGraphVisitor;
import io.quarkus.bootstrap.resolver.maven.DeploymentInjectingDependencyVisitor;
import io.quarkus.bootstrap.resolver.maven.DeploymentInjectionException;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.SimpleDependencyGraphTransformationContext;

Expand Down Expand Up @@ -170,8 +171,8 @@ public boolean visitLeave(DependencyNode node) {
final DeploymentInjectingDependencyVisitor deploymentInjector = new DeploymentInjectingDependencyVisitor(mvn,
managedDeps, mvn.aggregateRepositories(managedRepos, mvn.newResolutionRepositories(mvn.resolveDescriptor(toAetherArtifact(appArtifact)).getRepositories())));
try {
resolvedDeps.accept(new TreeDependencyVisitor(deploymentInjector));
} catch (DeploymentInjectionException e) {
deploymentInjector.injectDeploymentDependencies(resolvedDeps);
} catch (BootstrapDependencyProcessingException e) {
throw new AppModelResolverException("Failed to inject extension deployment dependencies for " + resolvedDeps.getArtifact(), e.getCause());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void visit(DependencyNode node) {
private void visitEnter(DependencyNode node) {
final Dependency dep = node.getDependency();
if (deploymentNode == null) {
runtimeArtifact = DeploymentInjectingDependencyVisitor.getInjectedDependency(node);
runtimeArtifact = DeploymentInjectingDependencyVisitor.getRuntimeArtifact(node);
if (runtimeArtifact != null) {
deploymentNode = node;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.graph.DependencyVisitor;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
Expand All @@ -26,106 +26,123 @@
*
* @author Alexey Loubyansky
*/
public class DeploymentInjectingDependencyVisitor implements DependencyVisitor {
public class DeploymentInjectingDependencyVisitor {


private static final Logger log = Logger.getLogger(DeploymentInjectingDependencyVisitor.class);

static final String INJECTED_DEPENDENCY = "injected.dep";
static final String QUARKUS_RUNTIME_ARTIFACT = "quarkus.runtime";
private static final String QUARKUS_DEPLOYMENT_ARTIFACT = "quarkus.deployment";

public static Artifact getInjectedDependency(DependencyNode dep) {
return (Artifact) dep.getData().get(DeploymentInjectingDependencyVisitor.INJECTED_DEPENDENCY);
public static Artifact getRuntimeArtifact(DependencyNode dep) {
return (Artifact) dep.getData().get(DeploymentInjectingDependencyVisitor.QUARKUS_RUNTIME_ARTIFACT);
}

private final MavenArtifactResolver resolver;
private final List<Dependency> managedDeps;
private final List<RemoteRepository> mainRepos;
private DependencyNode node;

boolean injectedDeps;

private List<DependencyNode> runtimeNodes = new ArrayList<>();

public DeploymentInjectingDependencyVisitor(MavenArtifactResolver resolver, List<Dependency> managedDeps, List<RemoteRepository> mainRepos) {
this.resolver = resolver;
this.managedDeps = managedDeps;
this.managedDeps = managedDeps.isEmpty() ? new ArrayList<>() : managedDeps;
this.mainRepos = mainRepos;
}

public boolean isInjectedDeps() {
return injectedDeps;
}

@Override
public boolean visitEnter(DependencyNode node) {
public void injectDeploymentDependencies(DependencyNode root) throws BootstrapDependencyProcessingException {
collectRuntimeExtensions(root.getChildren());
// resolve and inject deployment dependencies
for(DependencyNode rtNode : runtimeNodes) {
replaceWith(rtNode, collectDependencies((Artifact)rtNode.getData().get(QUARKUS_DEPLOYMENT_ARTIFACT)));
}
}

public void collectRuntimeExtensions(List<DependencyNode> list) {
if(list.isEmpty()) {
return;
}
int i = 0;
while (i < list.size()) {
collectRuntimeExtensions(list.get(i++));
}
}

private void collectRuntimeExtensions(DependencyNode node) {
final Artifact artifact = node.getArtifact();
if(!artifact.getExtension().equals("jar")) {
return true;
return;
}
this.node = node;

boolean processChildren = true;
final Path path = resolve(artifact);
try {
if (Files.isDirectory(path)) {
processChildren = !processMetaInfDir(path.resolve(BootstrapConstants.META_INF));
processMetaInfDir(node, path.resolve(BootstrapConstants.META_INF));
} else {
try (FileSystem artifactFs = ZipUtils.newFileSystem(path)) {
processChildren = !processMetaInfDir(artifactFs.getPath(BootstrapConstants.META_INF));
processMetaInfDir(node, artifactFs.getPath(BootstrapConstants.META_INF));
}
}
} catch (Throwable t) {
} catch (Exception t) {
throw new DeploymentInjectionException("Failed to inject extension deplpyment dependencies", t);
}
return processChildren;
}

@Override
public boolean visitLeave(DependencyNode node) {
return true;
collectRuntimeExtensions(node.getChildren());
}

private boolean processMetaInfDir(Path metaInfDir) throws BootstrapDependencyProcessingException {
private void processMetaInfDir(DependencyNode node, Path metaInfDir) throws BootstrapDependencyProcessingException {
if (!Files.exists(metaInfDir)) {
return false;
return;
}
final Path p = metaInfDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
if (!Files.exists(p)) {
return false;
return;
}
processPlatformArtifact(p);
return true;
processPlatformArtifact(node, p);
}

private void processPlatformArtifact(Path descriptor) throws BootstrapDependencyProcessingException {
private void processPlatformArtifact(DependencyNode node, Path descriptor) throws BootstrapDependencyProcessingException {
final Properties rtProps = resolveDescriptor(descriptor);
if(rtProps == null) {
return;
}
log.debugf("Processing Quarkus extension %s", node);

String value = rtProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT);
final String value = rtProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT);
if(value == null) {
return;
}
if(value != null) {
replaceWith(collectDependencies(toArtifact(value)));
Artifact deploymentArtifact = toArtifact(value);
if(deploymentArtifact.getVersion() == null || deploymentArtifact.getVersion().isEmpty()) {
deploymentArtifact = deploymentArtifact.setVersion(node.getArtifact().getVersion());
}
node.setData(QUARKUS_DEPLOYMENT_ARTIFACT, deploymentArtifact);
runtimeNodes.add(node);
managedDeps.add(new Dependency(node.getArtifact(), JavaScopes.COMPILE));
managedDeps.add(new Dependency(deploymentArtifact, JavaScopes.COMPILE));
}
}

private void replaceWith(DependencyNode depNode) throws BootstrapDependencyProcessingException {
List<DependencyNode> children = depNode.getChildren();
private void replaceWith(DependencyNode originalNode, DependencyNode newNode) throws BootstrapDependencyProcessingException {
List<DependencyNode> children = newNode.getChildren();
if (children.isEmpty()) {
throw new BootstrapDependencyProcessingException(
"No dependencies collected for Quarkus extension deployment artifact " + depNode.getArtifact()
+ " while at least the corresponding runtime artifact " + node.getArtifact() + " is expected");
}
log.debugf("Injecting deployment dependency %s", depNode);
node.setData(INJECTED_DEPENDENCY, node.getArtifact());
node.setArtifact(depNode.getArtifact());
node.getDependency().setArtifact(depNode.getArtifact());
node.setChildren(children);
"No dependencies collected for Quarkus extension deployment artifact " + newNode.getArtifact()
+ " while at least the corresponding runtime artifact " + originalNode.getArtifact() + " is expected");
}
log.debugf("Injecting deployment dependency %s", newNode);

originalNode.setData(QUARKUS_RUNTIME_ARTIFACT, originalNode.getArtifact());
originalNode.setArtifact(newNode.getArtifact());
originalNode.getDependency().setArtifact(newNode.getArtifact());
originalNode.setChildren(children);
injectedDeps = true;
}

private DependencyNode collectDependencies(Artifact artifact) throws BootstrapDependencyProcessingException {
if(artifact.getVersion().isEmpty()) {
artifact = artifact.setVersion(node.getArtifact().getVersion());
}
try {
return managedDeps.isEmpty() ? resolver.collectDependencies(artifact, Collections.emptyList(), mainRepos).getRoot()
: resolver.collectManagedDependencies(artifact, Collections.emptyList(), managedDeps, mainRepos).getRoot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public abstract class CollectDependenciesBase extends ResolverSetupCleanup {

protected TsArtifact root;
protected List<AppDependency> expectedResult = Collections.emptyList();
protected List<AppDependency> deploymentDeps = Collections.emptyList();

@Override
@BeforeEach
Expand All @@ -33,8 +34,17 @@ public void setup() throws Exception {
@Test
public void testCollectedDependencies() throws Exception {
install(root);

List<AppDependency> expected;
if(deploymentDeps.isEmpty()) {
expected = expectedResult;
} else {
expected = new ArrayList<>(expectedResult.size() + deploymentDeps.size());
expected.addAll(expectedResult);
expected.addAll(deploymentDeps);
}
final List<AppDependency> resolvedDeps = resolver.resolveModel(root.toAppArtifact()).getAllDependencies();
assertEquals(expectedResult, resolvedDeps);
assertEquals(expected, resolvedDeps);
}

protected TsArtifact install(TsArtifact dep, boolean collected) {
Expand All @@ -57,6 +67,25 @@ protected TsArtifact install(TsArtifact dep, Path p, String collectedInScope) {
return dep;
}

protected void install(TsQuarkusExt ext) {
install(ext, true);
}

protected void install(TsQuarkusExt ext, boolean collected) {
ext.install(repo);
if(collected) {
addCollectedDep(ext.getRuntime(), "compile", false);
addCollectedDeploymentDep(ext.getDeployment());
}
}

protected void installAsDep(TsQuarkusExt ext) {
ext.install(repo);
root.addDependency(ext);
addCollectedDep(ext.getRuntime(), "compile", false);
addCollectedDeploymentDep(ext.getDeployment());
}

protected void installAsDep(TsArtifact dep) {
installAsDep(dep, true);
}
Expand Down Expand Up @@ -102,6 +131,13 @@ protected void addCollectedDep(final TsArtifact artifact, final String scope, bo
expectedResult.add(new AppDependency(artifact.toAppArtifact(), scope, optional));
}

protected void addCollectedDeploymentDep(TsArtifact ext) {
if(deploymentDeps.isEmpty()) {
deploymentDeps = new ArrayList<>();
}
deploymentDeps.add(new AppDependency(ext.toAppArtifact(), "compile", false));
}

protected void addManagedDep(TsArtifact dep) {
root.addManagedDependency(new TsDependency(dep));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.quarkus.bootstrap.resolver;

import java.io.IOException;

import io.quarkus.bootstrap.BootstrapConstants;

public class TsQuarkusExt {
Expand Down Expand Up @@ -34,7 +32,7 @@ public TsQuarkusExt addDependency(TsQuarkusExt ext) {
return this;
}

public void install(TsRepoBuilder repo) throws IOException {
public void install(TsRepoBuilder repo) {
repo.install(deployment);
repo.install(runtime);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.quarkus.bootstrap.resolver.replace.test;

import io.quarkus.bootstrap.resolver.CollectDependenciesBase;
import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.bootstrap.resolver.TsQuarkusExt;

/**
*
* @author Alexey Loubyansky
*/
public class ManagedReplacedDependencyTestCase extends CollectDependenciesBase {

@Override
protected void setupDependencies() throws Exception {

// install ext 1.0.X in the repo
final TsQuarkusExt ext100 = new TsQuarkusExt("ext1", "100");
install(ext100, false);
final TsQuarkusExt ext101 = new TsQuarkusExt("ext1", "101");
install(ext101, false);
final TsQuarkusExt ext102 = new TsQuarkusExt("ext1", "102");
install(ext102, false);
final TsQuarkusExt ext103 = new TsQuarkusExt("ext1", "103");
install(ext103, true);

// install ext 2.0.0 and add it as a direct dependency
final TsQuarkusExt ext200 = new TsQuarkusExt("ext2", "200");
ext200.addDependency(ext100);
installAsDep(ext200);

// install ext 2.0.1 and add it to the dependency management
final TsQuarkusExt ext201 = new TsQuarkusExt("ext2", "201");
ext201.addDependency(ext101);
install(ext201, false);

// install ext 3.0.0
final TsQuarkusExt ext300 = new TsQuarkusExt("ext3", "300");
ext300.addDependency(ext200);
install(ext300, false);

// install ext 3.0.1
final TsQuarkusExt ext301 = new TsQuarkusExt("ext3", "301");
ext301.addDependency(ext201);
install(ext301, false);

// add a dependency on ext3 (no version)
root.addDependency(TsArtifact.jar(ext300.getRuntime().getArtifactId(), null));

// the dependency management
addManagedDep(ext103.getRuntime());
addManagedDep(ext201.getRuntime());
addManagedDep(ext301.getRuntime());

addCollectedDep(ext301.getRuntime());
addCollectedDeploymentDep(ext301.getDeployment());
}
}